libpetri 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-KO6TSB47.js → chunk-4HMFNYTH.js} +2 -2
- package/dist/{chunk-WYCGGQAW.js → chunk-75KEJQGC.js} +198 -58
- package/dist/chunk-75KEJQGC.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/doclet/index.d.ts +1 -1
- package/dist/doclet/resources/petrinet-diagrams.js +1 -1
- package/dist/{event-store-2FOAeUyh.d.ts → event-store-hlH3-bzJ.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/index.d.ts +46 -13
- package/dist/index.js +134 -75
- package/dist/index.js.map +1 -1
- package/dist/{petri-net-hduM6tJf.d.ts → petri-net-CH7UvjOW.d.ts} +55 -4
- package/dist/render-dom/index.js +1 -1
- package/dist/verification/index.d.ts +36 -47
- package/dist/verification/index.js +3 -1
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.js +1 -1
- package/dist/viewer/viewer.iife.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WYCGGQAW.js.map +0 -1
- /package/dist/{chunk-KO6TSB47.js.map → chunk-4HMFNYTH.js.map} +0 -0
|
@@ -762,7 +762,7 @@ function stripClusterEdgeAttrs(line) {
|
|
|
762
762
|
}
|
|
763
763
|
|
|
764
764
|
// src/viewer/version.ts
|
|
765
|
-
var VERSION = true ? "
|
|
765
|
+
var VERSION = true ? "5.0.0" : "0.0.0-dev";
|
|
766
766
|
|
|
767
767
|
// src/viewer/index.ts
|
|
768
768
|
async function mount(dotSource, container, opts = {}) {
|
|
@@ -1013,4 +1013,4 @@ export {
|
|
|
1013
1013
|
VERSION,
|
|
1014
1014
|
mount
|
|
1015
1015
|
};
|
|
1016
|
-
//# sourceMappingURL=chunk-
|
|
1016
|
+
//# sourceMappingURL=chunk-4HMFNYTH.js.map
|
|
@@ -331,6 +331,9 @@ var MarkingStateBuilder = class {
|
|
|
331
331
|
function deadlockFree() {
|
|
332
332
|
return { type: "deadlock-free" };
|
|
333
333
|
}
|
|
334
|
+
function terminatesAtSink() {
|
|
335
|
+
return { type: "terminates-at-sink" };
|
|
336
|
+
}
|
|
334
337
|
function mutualExclusion(p1, p2) {
|
|
335
338
|
return { type: "mutual-exclusion", p1, p2 };
|
|
336
339
|
}
|
|
@@ -350,6 +353,8 @@ function propertyDescription(prop) {
|
|
|
350
353
|
switch (prop.type) {
|
|
351
354
|
case "deadlock-free":
|
|
352
355
|
return "Deadlock-freedom";
|
|
356
|
+
case "terminates-at-sink":
|
|
357
|
+
return "Terminates at a declared sink";
|
|
353
358
|
case "mutual-exclusion":
|
|
354
359
|
return `Mutual exclusion of ${prop.p1.name} and ${prop.p2.name}`;
|
|
355
360
|
case "place-bound":
|
|
@@ -1545,8 +1550,31 @@ function indexOrdered(flatNet, places) {
|
|
|
1545
1550
|
}
|
|
1546
1551
|
function encodePropertyViolation(flatNet, property, mVars, sinkPlaces, envInject) {
|
|
1547
1552
|
switch (property.type) {
|
|
1548
|
-
|
|
1549
|
-
|
|
1553
|
+
// DeadlockFree (VER-002): a quiescent marking that STRANDS a token — holds one
|
|
1554
|
+
// in a place that is not a declared sink. The empty marking strands nothing and
|
|
1555
|
+
// is therefore not a violation (AC4).
|
|
1556
|
+
case "deadlock-free": {
|
|
1557
|
+
const conditions = encodeQuiescent(flatNet, mVars, envInject);
|
|
1558
|
+
if (conditions == null) return "false";
|
|
1559
|
+
const sinks = new Set(indexOrdered(flatNet, sinkPlaces));
|
|
1560
|
+
const stranded = [];
|
|
1561
|
+
for (let pid = 0; pid < flatNet.places.length; pid++) {
|
|
1562
|
+
if (!sinks.has(pid)) stranded.push(`(>= ${mVars[pid]} 1)`);
|
|
1563
|
+
}
|
|
1564
|
+
if (stranded.length === 0) return "false";
|
|
1565
|
+
conditions.push(`(or ${stranded.join(" ")})`);
|
|
1566
|
+
return joinConditions(conditions);
|
|
1567
|
+
}
|
|
1568
|
+
// TerminatesAtSink (VER-002): a quiescent marking that reached NO declared sink.
|
|
1569
|
+
// This is the predicate DeadlockFree carried before the VER-002 split, unchanged.
|
|
1570
|
+
case "terminates-at-sink": {
|
|
1571
|
+
const conditions = encodeQuiescent(flatNet, mVars, envInject);
|
|
1572
|
+
if (conditions == null) return "false";
|
|
1573
|
+
for (const pid of indexOrdered(flatNet, sinkPlaces)) {
|
|
1574
|
+
conditions.push(`(= ${mVars[pid]} 0)`);
|
|
1575
|
+
}
|
|
1576
|
+
return joinConditions(conditions);
|
|
1577
|
+
}
|
|
1550
1578
|
case "mutual-exclusion": {
|
|
1551
1579
|
const conditions = indexOrdered(flatNet, [property.p1, property.p2]).map((i) => `(>= ${mVars[i]} 1)`);
|
|
1552
1580
|
return conditions.length === 0 ? "false" : `(and ${conditions.join(" ")})`;
|
|
@@ -1560,14 +1588,23 @@ function encodePropertyViolation(flatNet, property, mVars, sinkPlaces, envInject
|
|
|
1560
1588
|
const conditions = indexOrdered(flatNet, property.places).map((i) => `(>= ${mVars[i]} 1)`);
|
|
1561
1589
|
return conditions.length === 0 ? "false" : `(and ${conditions.join(" ")})`;
|
|
1562
1590
|
}
|
|
1591
|
+
// JoinedOrDeadLettered (NU-040 AC4): a quiescent state that still holds a
|
|
1592
|
+
// `pending` token is a stranded correlation group. Carries NO sink clause — a
|
|
1593
|
+
// declared sink must not excuse a stranded group.
|
|
1563
1594
|
case "joined-or-dead-lettered": {
|
|
1564
|
-
const deadlock = encodeDeadlock(flatNet, mVars, sinkPlaces, envInject);
|
|
1565
1595
|
const pid = flatNet.placeIndex.get(property.pending.name);
|
|
1566
|
-
|
|
1596
|
+
if (pid == null) return "false";
|
|
1597
|
+
const conditions = encodeQuiescent(flatNet, mVars, envInject);
|
|
1598
|
+
if (conditions == null) return "false";
|
|
1599
|
+
conditions.push(`(>= ${mVars[pid]} 1)`);
|
|
1600
|
+
return joinConditions(conditions);
|
|
1567
1601
|
}
|
|
1568
1602
|
}
|
|
1569
1603
|
}
|
|
1570
|
-
function
|
|
1604
|
+
function joinConditions(conditions) {
|
|
1605
|
+
return conditions.length === 0 ? "true" : `(and ${conditions.join("\n ")})`;
|
|
1606
|
+
}
|
|
1607
|
+
function encodeQuiescent(flatNet, mVars, envInject) {
|
|
1571
1608
|
const envBound = /* @__PURE__ */ new Map();
|
|
1572
1609
|
for (const inj of envInject) envBound.set(inj.pid, inj.bound);
|
|
1573
1610
|
const disabledConditions = [];
|
|
@@ -1597,13 +1634,10 @@ function encodeDeadlock(flatNet, mVars, sinkPlaces, envInject) {
|
|
|
1597
1634
|
disabledConditions.push("true");
|
|
1598
1635
|
continue;
|
|
1599
1636
|
}
|
|
1600
|
-
if (disableReasons.length === 0) return
|
|
1637
|
+
if (disableReasons.length === 0) return null;
|
|
1601
1638
|
disabledConditions.push(`(or ${disableReasons.join(" ")})`);
|
|
1602
1639
|
}
|
|
1603
|
-
|
|
1604
|
-
disabledConditions.push(`(= ${mVars[pid]} 0)`);
|
|
1605
|
-
}
|
|
1606
|
-
return disabledConditions.length === 0 ? "true" : `(and ${disabledConditions.join("\n ")})`;
|
|
1640
|
+
return disabledConditions;
|
|
1607
1641
|
}
|
|
1608
1642
|
function injectionMap(flatNet) {
|
|
1609
1643
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -2511,20 +2545,38 @@ function enabledRelaxEnv(state, ft, envInj) {
|
|
|
2511
2545
|
}
|
|
2512
2546
|
return true;
|
|
2513
2547
|
}
|
|
2514
|
-
function
|
|
2548
|
+
function isQuiescent(index, state) {
|
|
2515
2549
|
for (const ft of index.flatNet.transitions) {
|
|
2516
2550
|
if (enabledRelaxEnv(state, ft, index.envInj)) return false;
|
|
2517
2551
|
}
|
|
2518
2552
|
return true;
|
|
2519
2553
|
}
|
|
2554
|
+
function sinkIndices(flatNet, sinkPlaces) {
|
|
2555
|
+
const idx = /* @__PURE__ */ new Set();
|
|
2556
|
+
for (const sink of sinkPlaces) {
|
|
2557
|
+
const i = flatNetIndexOf(flatNet, sink);
|
|
2558
|
+
if (i >= 0) idx.add(i);
|
|
2559
|
+
}
|
|
2560
|
+
return idx;
|
|
2561
|
+
}
|
|
2520
2562
|
function satisfiesBadIndexed(index, state, property, sinkPlaces) {
|
|
2521
2563
|
const flatNet = index.flatNet;
|
|
2522
2564
|
switch (property.type) {
|
|
2565
|
+
// DeadlockFree (VER-002): quiescent AND some marked place is not a declared
|
|
2566
|
+
// sink. Mirrors the encoder's `stranded` disjunction.
|
|
2523
2567
|
case "deadlock-free": {
|
|
2524
|
-
if (!
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
if (
|
|
2568
|
+
if (!isQuiescent(index, state)) return false;
|
|
2569
|
+
const sinks = sinkIndices(flatNet, sinkPlaces);
|
|
2570
|
+
for (let pid = 0; pid < flatNet.places.length; pid++) {
|
|
2571
|
+
if (!sinks.has(pid) && state[pid] >= 1) return true;
|
|
2572
|
+
}
|
|
2573
|
+
return false;
|
|
2574
|
+
}
|
|
2575
|
+
// TerminatesAtSink (VER-002): quiescent AND no declared sink marked.
|
|
2576
|
+
case "terminates-at-sink": {
|
|
2577
|
+
if (!isQuiescent(index, state)) return false;
|
|
2578
|
+
for (const pid of sinkIndices(flatNet, sinkPlaces)) {
|
|
2579
|
+
if (state[pid] !== 0) return false;
|
|
2528
2580
|
}
|
|
2529
2581
|
return true;
|
|
2530
2582
|
}
|
|
@@ -2540,10 +2592,12 @@ function satisfiesBadIndexed(index, state, property, sinkPlaces) {
|
|
|
2540
2592
|
if (idx < 0) return false;
|
|
2541
2593
|
return state[idx] > property.bound;
|
|
2542
2594
|
}
|
|
2595
|
+
// JoinedOrDeadLettered (NU-040 AC4): quiescent AND `pending` marked. No sink
|
|
2596
|
+
// clause — a marked sink must not excuse a stranded group.
|
|
2543
2597
|
case "joined-or-dead-lettered": {
|
|
2544
2598
|
const idx = flatNetIndexOf(flatNet, property.pending);
|
|
2545
2599
|
if (idx < 0) return false;
|
|
2546
|
-
return
|
|
2600
|
+
return isQuiescent(index, state) && state[idx] >= 1;
|
|
2547
2601
|
}
|
|
2548
2602
|
case "unreachable": {
|
|
2549
2603
|
let resolved = 0;
|
|
@@ -2914,13 +2968,38 @@ function encodeViolation(plan, lay, flat, property, sinkPlaces, envInj) {
|
|
|
2914
2968
|
return anyPlacePresent([property.p1, property.p2]);
|
|
2915
2969
|
case "unreachable":
|
|
2916
2970
|
return anyPlacePresent(property.places);
|
|
2917
|
-
|
|
2918
|
-
|
|
2971
|
+
// DeadlockFree (VER-002): quiescent AND some marked place is not a declared
|
|
2972
|
+
// sink. Mirrors the flat encoder's `stranded` disjunction.
|
|
2973
|
+
case "deadlock-free": {
|
|
2974
|
+
const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
|
|
2975
|
+
if (conds == null) return "false";
|
|
2976
|
+
const sinks = new Set(indexOrdered(flat, sinkPlaces));
|
|
2977
|
+
const stranded = [];
|
|
2978
|
+
for (let pid = 0; pid < flat.places.length; pid++) {
|
|
2979
|
+
if (!sinks.has(pid)) stranded.push(`(>= ${aggregate(plan, lay, pid, lay.cur)} 1)`);
|
|
2980
|
+
}
|
|
2981
|
+
if (stranded.length === 0) return "false";
|
|
2982
|
+
conds.push(`(or ${stranded.join(" ")})`);
|
|
2983
|
+
return joinColoured(conds);
|
|
2984
|
+
}
|
|
2985
|
+
// TerminatesAtSink (VER-002): quiescent AND no declared sink marked.
|
|
2986
|
+
case "terminates-at-sink": {
|
|
2987
|
+
const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
|
|
2988
|
+
if (conds == null) return "false";
|
|
2989
|
+
for (const pid of indexOrdered(flat, sinkPlaces)) {
|
|
2990
|
+
conds.push(`(= ${aggregate(plan, lay, pid, lay.cur)} 0)`);
|
|
2991
|
+
}
|
|
2992
|
+
return joinColoured(conds);
|
|
2993
|
+
}
|
|
2994
|
+
// JoinedOrDeadLettered (NU-040 AC4): quiescent AND `pending` marked, with NO
|
|
2995
|
+
// sink clause.
|
|
2919
2996
|
case "joined-or-dead-lettered": {
|
|
2920
2997
|
const pid = flat.placeIndex.get(property.pending.name);
|
|
2921
2998
|
if (pid == null) return null;
|
|
2922
|
-
const
|
|
2923
|
-
|
|
2999
|
+
const conds = encodeColouredQuiescent(plan, lay, flat, envInj);
|
|
3000
|
+
if (conds == null) return "false";
|
|
3001
|
+
conds.push(`(>= ${aggregate(plan, lay, pid, lay.cur)} 1)`);
|
|
3002
|
+
return joinColoured(conds);
|
|
2924
3003
|
}
|
|
2925
3004
|
}
|
|
2926
3005
|
}
|
|
@@ -2978,7 +3057,10 @@ function colouredDisabledTerm(cls, plan, lay) {
|
|
|
2978
3057
|
}
|
|
2979
3058
|
}
|
|
2980
3059
|
}
|
|
2981
|
-
function
|
|
3060
|
+
function joinColoured(conds) {
|
|
3061
|
+
return conds.length === 0 ? "true" : `(and ${conds.join(" ")})`;
|
|
3062
|
+
}
|
|
3063
|
+
function encodeColouredQuiescent(plan, lay, flat, envInj) {
|
|
2982
3064
|
const disabledConditions = [];
|
|
2983
3065
|
for (let ti = 0; ti < plan.classes.length; ti++) {
|
|
2984
3066
|
const cls = plan.classes[ti];
|
|
@@ -2991,13 +3073,10 @@ function encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj) {
|
|
|
2991
3073
|
}
|
|
2992
3074
|
const term = colouredDisabledTerm(cls, plan, lay);
|
|
2993
3075
|
if (term != null) reasons.push(term);
|
|
2994
|
-
if (reasons.length === 0) return
|
|
3076
|
+
if (reasons.length === 0) return null;
|
|
2995
3077
|
disabledConditions.push(reasons.length === 1 ? reasons[0] : `(or ${reasons.join(" ")})`);
|
|
2996
3078
|
}
|
|
2997
|
-
|
|
2998
|
-
disabledConditions.push(`(= ${aggregate(plan, lay, pid, lay.cur)} 0)`);
|
|
2999
|
-
}
|
|
3000
|
-
return disabledConditions.length === 0 ? "true" : `(and ${disabledConditions.join(" ")})`;
|
|
3079
|
+
return disabledConditions;
|
|
3001
3080
|
}
|
|
3002
3081
|
|
|
3003
3082
|
// src/verification/analysis/name-fragment.ts
|
|
@@ -3484,8 +3563,16 @@ function decide(scg, property, sinkPlaces) {
|
|
|
3484
3563
|
const m = scg.markingOf(i);
|
|
3485
3564
|
return m.hasTokens(property.p1) && m.hasTokens(property.p2);
|
|
3486
3565
|
});
|
|
3566
|
+
// DeadlockFree (VER-002): a quiescent class that strands a token — some marked
|
|
3567
|
+
// place is not a declared sink. The empty marking strands nothing (AC4).
|
|
3487
3568
|
case "deadlock-free":
|
|
3488
3569
|
return firstWhere((i) => scg.successorsOf(i).length === 0 && !allTokensInSinks(scg.markingOf(i), sinkPlaces));
|
|
3570
|
+
// TerminatesAtSink (VER-002): a quiescent class that marks NO declared sink.
|
|
3571
|
+
// Inverts with DeadlockFree on the empty marking, by design.
|
|
3572
|
+
case "terminates-at-sink":
|
|
3573
|
+
return firstWhere((i) => scg.successorsOf(i).length === 0 && !anySinkMarked(scg.markingOf(i), sinkPlaces));
|
|
3574
|
+
// JoinedOrDeadLettered (NU-040 AC4): a quiescent class still holding a pending
|
|
3575
|
+
// token. No sink clause.
|
|
3489
3576
|
case "joined-or-dead-lettered":
|
|
3490
3577
|
return firstWhere((i) => scg.successorsOf(i).length === 0 && scg.markingOf(i).hasTokens(property.pending));
|
|
3491
3578
|
}
|
|
@@ -3498,6 +3585,14 @@ function allTokensInSinks(m, sinks) {
|
|
|
3498
3585
|
}
|
|
3499
3586
|
return true;
|
|
3500
3587
|
}
|
|
3588
|
+
function anySinkMarked(m, sinks) {
|
|
3589
|
+
const sinkNames = /* @__PURE__ */ new Set();
|
|
3590
|
+
for (const s of sinks) sinkNames.add(s.name);
|
|
3591
|
+
for (const p of m.placesWithTokens()) {
|
|
3592
|
+
if (sinkNames.has(p.name)) return true;
|
|
3593
|
+
}
|
|
3594
|
+
return false;
|
|
3595
|
+
}
|
|
3501
3596
|
function counterexamplePath(scg, target) {
|
|
3502
3597
|
const n = scg.classCount();
|
|
3503
3598
|
const parent = new Array(n).fill(-1);
|
|
@@ -3528,6 +3623,7 @@ function counterexamplePath(scg, target) {
|
|
|
3528
3623
|
}
|
|
3529
3624
|
|
|
3530
3625
|
// src/verification/smt-verifier.ts
|
|
3626
|
+
var IGNORE_MODE_VACUITY_REASON = "environment places present but not modeled (mode=ignore); a proof would be vacuous \u2014 use alwaysAvailable() or bounded(k) to model external injection";
|
|
3531
3627
|
var SmtVerifier = class _SmtVerifier {
|
|
3532
3628
|
constructor(net) {
|
|
3533
3629
|
this.net = net;
|
|
@@ -3636,13 +3732,26 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3636
3732
|
* into a chain whose other combinations avoid it (dropped by the H1 guard). On a
|
|
3637
3733
|
* net with a few reset arcs that can lose every law of the chains those arcs
|
|
3638
3734
|
* touch, and without them IC3 has to rediscover the conservation of each chain —
|
|
3639
|
-
* on a ~100-place net it does not within any practical budget.
|
|
3640
|
-
*
|
|
3735
|
+
* on a ~100-place net it does not within any practical budget.
|
|
3736
|
+
*
|
|
3737
|
+
* **Turn this on if the net has any `all()` / `atLeast(n)` or reset arc on a busy
|
|
3738
|
+
* place** — draining an input queue is the everyday case. Every basis row whose
|
|
3739
|
+
* support touches such a place fails the H1 guard and is dropped, so the encoders
|
|
3740
|
+
* run on a deficient invariant set and nothing in the report says a law is missing
|
|
3741
|
+
* beyond the `Dropped` lines.
|
|
3742
|
+
*
|
|
3743
|
+
* This reaches the **name-coloured** encoder (NU-050) as well as the flat one, and
|
|
3744
|
+
* it matters most there. On a 113-place ν-net, whole-net deadlock-freedom went from
|
|
3745
|
+
* `unknown` after 50 minutes to `proven` in about 15 seconds with this option as the
|
|
3746
|
+
* only change; on the flat path, reachability-safety queries that timed out at 120 s
|
|
3747
|
+
* close in about a second.
|
|
3641
3748
|
*
|
|
3642
3749
|
* Soundness is unchanged: the semiflows pass the same exact re-validation as the
|
|
3643
3750
|
* basis rows, the union is pure strengthening (`Semiflow.lean`,
|
|
3644
3751
|
* `semiflow_union_sound`), and the certificate check re-proves the strengthened
|
|
3645
|
-
* invariant
|
|
3752
|
+
* invariant — that check is flat-path only, so a coloured `proven` reports
|
|
3753
|
+
* `Certificate check: not applicable (name-coloured encoding)`. Off by default so
|
|
3754
|
+
* reports stay byte-equal.
|
|
3646
3755
|
*/
|
|
3647
3756
|
semiflowInvariants(enabled) {
|
|
3648
3757
|
this._semiflowInvariants = enabled;
|
|
@@ -3702,6 +3811,48 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3702
3811
|
this._prioritySemantics = semantics;
|
|
3703
3812
|
return this;
|
|
3704
3813
|
}
|
|
3814
|
+
/**
|
|
3815
|
+
* The name-coloured plan and its encoding, or a null plan when the net is outside
|
|
3816
|
+
* the fragment (NU-050) and a null encoding when the property names a place the net
|
|
3817
|
+
* does not resolve.
|
|
3818
|
+
*
|
|
3819
|
+
* {@link verify} and {@link encodeScripts} share this deliberately. They used to
|
|
3820
|
+
* invoke `buildColouredPlan` and `encodeColoured` separately, so handing the encoder
|
|
3821
|
+
* the wrong one of the two lists changed only one of them — and the script-parity
|
|
3822
|
+
* goldens are generated from `encodeScripts`. Unifying the invocation closes that. It
|
|
3823
|
+
* does not make the two paths identical: each still computes its own invariant and
|
|
3824
|
+
* semiflow lists, so they can still drift through the arguments rather than the call.
|
|
3825
|
+
*
|
|
3826
|
+
* `invariants` is what the encoder conjoins into every rule body (the null-space
|
|
3827
|
+
* basis, unioned with the semiflows when VER-007 is enabled); `semiflows` sets the
|
|
3828
|
+
* colour-slot bound k (NU-053). They are not the same list.
|
|
3829
|
+
*/
|
|
3830
|
+
colouredAttempt(flatNet, invariants, semiflows) {
|
|
3831
|
+
const hasMatch = [...this.net.transitions].some((t) => t.matchSpec !== null);
|
|
3832
|
+
const nuBounded = this._budgetPlaces.size > 0;
|
|
3833
|
+
if (!hasMatch || !nuBounded) return { plan: null, encoding: null };
|
|
3834
|
+
const plan = buildColouredPlan(
|
|
3835
|
+
this.net,
|
|
3836
|
+
flatNet,
|
|
3837
|
+
this._initialMarking,
|
|
3838
|
+
this._budgetPlaces,
|
|
3839
|
+
this._fragmentMode,
|
|
3840
|
+
this._carrierPlaces,
|
|
3841
|
+
semiflows
|
|
3842
|
+
);
|
|
3843
|
+
if (plan == null) return { plan: null, encoding: null };
|
|
3844
|
+
return {
|
|
3845
|
+
plan,
|
|
3846
|
+
encoding: encodeColoured(
|
|
3847
|
+
plan,
|
|
3848
|
+
flatNet,
|
|
3849
|
+
this._initialMarking,
|
|
3850
|
+
this._property,
|
|
3851
|
+
invariants,
|
|
3852
|
+
this._sinkPlaces
|
|
3853
|
+
)
|
|
3854
|
+
};
|
|
3855
|
+
}
|
|
3705
3856
|
/**
|
|
3706
3857
|
* The SMT-LIB2 scripts {@link verify} would send to z3 for this configuration,
|
|
3707
3858
|
* without running a solver (VER-013 AC1): the HORN query (flat, or name-coloured
|
|
@@ -3713,8 +3864,6 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3713
3864
|
*/
|
|
3714
3865
|
encodeScripts() {
|
|
3715
3866
|
requireOutputProducingActions(this.net);
|
|
3716
|
-
const hasMatch = [...this.net.transitions].some((t) => t.matchSpec !== null);
|
|
3717
|
-
const nuBounded = this._budgetPlaces.size > 0;
|
|
3718
3867
|
const flatNet = flatten(this.net, this._environmentPlaces, this._environmentMode);
|
|
3719
3868
|
const matrix = IncidenceMatrix.from(flatNet);
|
|
3720
3869
|
const { valid: basis } = validateInvariantsExact(
|
|
@@ -3732,20 +3881,9 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3732
3881
|
let invariants = basis;
|
|
3733
3882
|
if (this._semiflowInvariants) invariants = strengthenWithSemiflows(basis, semiflows).invariants;
|
|
3734
3883
|
invariants = canonicalInvariantOrder(invariants);
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
flatNet,
|
|
3739
|
-
this._initialMarking,
|
|
3740
|
-
this._budgetPlaces,
|
|
3741
|
-
this._fragmentMode,
|
|
3742
|
-
this._carrierPlaces,
|
|
3743
|
-
semiflows
|
|
3744
|
-
);
|
|
3745
|
-
if (plan != null) {
|
|
3746
|
-
const coloured = encodeColoured(plan, flatNet, this._initialMarking, this._property, invariants, this._sinkPlaces);
|
|
3747
|
-
if (coloured != null) return { horn: coloured.smt2, certificate: null, coloured: true };
|
|
3748
|
-
}
|
|
3884
|
+
const attempt = this.colouredAttempt(flatNet, invariants, semiflows);
|
|
3885
|
+
if (attempt.encoding != null) {
|
|
3886
|
+
return { horn: attempt.encoding.smt2, certificate: null, coloured: true };
|
|
3749
3887
|
}
|
|
3750
3888
|
const horn = encode(flatNet, this._initialMarking, this._property, invariants, this._sinkPlaces, this._counterexampleReplay).smt2;
|
|
3751
3889
|
const certificate = vcScript(
|
|
@@ -3796,8 +3934,13 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3796
3934
|
if (outcome.transitions.length > 0) {
|
|
3797
3935
|
report.push(` Counterexample trace: ${outcome.trace.length} states, ${outcome.transitions.length} transitions`);
|
|
3798
3936
|
}
|
|
3937
|
+
let routeBVerdict = outcome.verdict;
|
|
3938
|
+
if (routeBVerdict.type === "proven" && this._environmentPlaces.size > 0 && this._environmentMode.type === "ignore") {
|
|
3939
|
+
report.push(` Downgraded to UNKNOWN: ${IGNORE_MODE_VACUITY_REASON}`);
|
|
3940
|
+
routeBVerdict = { type: "unknown", reason: IGNORE_MODE_VACUITY_REASON };
|
|
3941
|
+
}
|
|
3799
3942
|
return buildResult(
|
|
3800
|
-
|
|
3943
|
+
routeBVerdict,
|
|
3801
3944
|
report.join("\n"),
|
|
3802
3945
|
[],
|
|
3803
3946
|
[],
|
|
@@ -3903,15 +4046,6 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3903
4046
|
}
|
|
3904
4047
|
report.push("");
|
|
3905
4048
|
report.push("Phase 4: IC3/PDR verification via Z3 Spacer...");
|
|
3906
|
-
const colouredPlan = hasMatch && nuBounded ? buildColouredPlan(
|
|
3907
|
-
this.net,
|
|
3908
|
-
flatNet,
|
|
3909
|
-
this._initialMarking,
|
|
3910
|
-
this._budgetPlaces,
|
|
3911
|
-
this._fragmentMode,
|
|
3912
|
-
this._carrierPlaces,
|
|
3913
|
-
semiflows
|
|
3914
|
-
) : null;
|
|
3915
4049
|
const stats = {
|
|
3916
4050
|
places: flatNet.places.length,
|
|
3917
4051
|
transitions: flatNet.transitions.length,
|
|
@@ -3932,12 +4066,14 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3932
4066
|
return buildResult({ type: "unknown", reason }, report.join("\n"), invariants, [], [], [], performance.now() - start, stats);
|
|
3933
4067
|
}
|
|
3934
4068
|
report.push(` Solver: z3 ${formatZ3Version(solver.version)}`);
|
|
4069
|
+
const colouredAttempt = this.colouredAttempt(flatNet, invariants, semiflows);
|
|
4070
|
+
const colouredPlan = colouredAttempt.plan;
|
|
3935
4071
|
let encoding;
|
|
3936
4072
|
if (colouredPlan != null) {
|
|
3937
4073
|
report.push(
|
|
3938
4074
|
` \u03BD-encoding: name-coloured (exact within budget k=${colouredPlan.k}; ${colouredPlan.coloured.length} coloured place(s))`
|
|
3939
4075
|
);
|
|
3940
|
-
const coloured =
|
|
4076
|
+
const coloured = colouredAttempt.encoding;
|
|
3941
4077
|
if (coloured == null) {
|
|
3942
4078
|
const reason = "property names a place that does not resolve in the net; refusing to certify (the encoding would be vacuously proven)";
|
|
3943
4079
|
report.push(" Status: UNKNOWN (unresolved property place)\n");
|
|
@@ -3966,7 +4102,7 @@ var SmtVerifier = class _SmtVerifier {
|
|
|
3966
4102
|
switch (queryResult.type) {
|
|
3967
4103
|
case "proven": {
|
|
3968
4104
|
if (this._environmentPlaces.size > 0 && this._environmentMode.type === "ignore") {
|
|
3969
|
-
const reason =
|
|
4105
|
+
const reason = IGNORE_MODE_VACUITY_REASON;
|
|
3970
4106
|
report.push(` Status: UNSAT, but vacuous under ignore mode
|
|
3971
4107
|
`);
|
|
3972
4108
|
report.push("=== RESULT ===\n");
|
|
@@ -4165,6 +4301,7 @@ function isReachabilitySafety(property) {
|
|
|
4165
4301
|
case "unreachable":
|
|
4166
4302
|
return true;
|
|
4167
4303
|
case "deadlock-free":
|
|
4304
|
+
case "terminates-at-sink":
|
|
4168
4305
|
case "joined-or-dead-lettered":
|
|
4169
4306
|
return false;
|
|
4170
4307
|
}
|
|
@@ -4241,6 +4378,8 @@ function unresolvedPropertyPlace(flatNet, property) {
|
|
|
4241
4378
|
switch (property.type) {
|
|
4242
4379
|
case "deadlock-free":
|
|
4243
4380
|
return [];
|
|
4381
|
+
case "terminates-at-sink":
|
|
4382
|
+
return [];
|
|
4244
4383
|
case "mutual-exclusion":
|
|
4245
4384
|
return [property.p1, property.p2];
|
|
4246
4385
|
case "place-bound":
|
|
@@ -4309,6 +4448,7 @@ export {
|
|
|
4309
4448
|
MarkingState,
|
|
4310
4449
|
MarkingStateBuilder,
|
|
4311
4450
|
deadlockFree,
|
|
4451
|
+
terminatesAtSink,
|
|
4312
4452
|
mutualExclusion,
|
|
4313
4453
|
placeBound,
|
|
4314
4454
|
unreachable,
|
|
@@ -4362,4 +4502,4 @@ export {
|
|
|
4362
4502
|
isProven,
|
|
4363
4503
|
isViolated
|
|
4364
4504
|
};
|
|
4365
|
-
//# sourceMappingURL=chunk-
|
|
4505
|
+
//# sourceMappingURL=chunk-75KEJQGC.js.map
|