libpetri 4.1.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.
@@ -762,7 +762,7 @@ function stripClusterEdgeAttrs(line) {
762
762
  }
763
763
 
764
764
  // src/viewer/version.ts
765
- var VERSION = true ? "4.1.0" : "0.0.0-dev";
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-GJAHGHGT.js.map
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
- case "deadlock-free":
1549
- return encodeDeadlock(flatNet, mVars, sinkPlaces, envInject);
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
- return pid == null ? "false" : `(and ${deadlock} (>= ${mVars[pid]} 1))`;
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 encodeDeadlock(flatNet, mVars, sinkPlaces, envInject) {
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 "false";
1637
+ if (disableReasons.length === 0) return null;
1601
1638
  disabledConditions.push(`(or ${disableReasons.join(" ")})`);
1602
1639
  }
1603
- for (const pid of indexOrdered(flatNet, sinkPlaces)) {
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 isDeadlockA(index, state) {
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 (!isDeadlockA(index, state)) return false;
2525
- for (const sink of sinkPlaces) {
2526
- const idx = flatNetIndexOf(flatNet, sink);
2527
- if (idx >= 0 && state[idx] > 0) return false;
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 isDeadlockA(index, state) && state[idx] >= 1;
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
- case "deadlock-free":
2918
- return encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj);
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 deadlock = encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj);
2923
- return `(and ${deadlock} (>= ${aggregate(plan, lay, pid, lay.cur)} 1))`;
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 encodeColouredDeadlock(plan, lay, flat, sinkPlaces, envInj) {
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 "false";
3076
+ if (reasons.length === 0) return null;
2995
3077
  disabledConditions.push(reasons.length === 1 ? reasons[0] : `(or ${reasons.join(" ")})`);
2996
3078
  }
2997
- for (const pid of indexOrdered(flat, sinkPlaces)) {
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);
@@ -4206,6 +4301,7 @@ function isReachabilitySafety(property) {
4206
4301
  case "unreachable":
4207
4302
  return true;
4208
4303
  case "deadlock-free":
4304
+ case "terminates-at-sink":
4209
4305
  case "joined-or-dead-lettered":
4210
4306
  return false;
4211
4307
  }
@@ -4282,6 +4378,8 @@ function unresolvedPropertyPlace(flatNet, property) {
4282
4378
  switch (property.type) {
4283
4379
  case "deadlock-free":
4284
4380
  return [];
4381
+ case "terminates-at-sink":
4382
+ return [];
4285
4383
  case "mutual-exclusion":
4286
4384
  return [property.p1, property.p2];
4287
4385
  case "place-bound":
@@ -4350,6 +4448,7 @@ export {
4350
4448
  MarkingState,
4351
4449
  MarkingStateBuilder,
4352
4450
  deadlockFree,
4451
+ terminatesAtSink,
4353
4452
  mutualExclusion,
4354
4453
  placeBound,
4355
4454
  unreachable,
@@ -4403,4 +4502,4 @@ export {
4403
4502
  isProven,
4404
4503
  isViolated
4405
4504
  };
4406
- //# sourceMappingURL=chunk-FD3S4TZM.js.map
4505
+ //# sourceMappingURL=chunk-75KEJQGC.js.map