libpetri 2.7.1 → 2.9.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.
@@ -1,10 +1,14 @@
1
1
  import {
2
+ DBM,
2
3
  IncidenceMatrix,
3
4
  MarkingState,
4
5
  MarkingStateBuilder,
5
6
  SmtVerifier,
7
+ StateClass,
8
+ StateClassGraph,
6
9
  alwaysAvailable,
7
10
  bounded,
11
+ branchPlaceBound,
8
12
  computePInvariants,
9
13
  createSpacerRunner,
10
14
  deadlockFree,
@@ -22,6 +26,7 @@ import {
22
26
  isCoveredByInvariants,
23
27
  isProven,
24
28
  isViolated,
29
+ joinedOrDeadLettered,
25
30
  mutualExclusion,
26
31
  pInvariant,
27
32
  pInvariantToString,
@@ -29,222 +34,8 @@ import {
29
34
  propertyDescription,
30
35
  structuralCheck,
31
36
  unreachable
32
- } from "../chunk-ETNHEAS6.js";
33
- import {
34
- earliest,
35
- latest
36
- } from "../chunk-ATT7U5H5.js";
37
-
38
- // src/verification/analysis/dbm.ts
39
- var EPSILON = 1e-9;
40
- var DBM = class _DBM {
41
- bounds;
42
- dim;
43
- clockNames;
44
- _empty;
45
- constructor(bounds, dim, clockNames, empty) {
46
- this.bounds = bounds;
47
- this.dim = dim;
48
- this.clockNames = clockNames;
49
- this._empty = empty;
50
- }
51
- /** Creates an initial firing domain for enabled transitions. */
52
- static create(clockNames, lowerBounds, upperBounds) {
53
- const n = clockNames.length;
54
- const dim = n + 1;
55
- const bounds = makeMatrix(dim, Infinity);
56
- for (let i = 0; i < n; i++) {
57
- bounds[0 * dim + (i + 1)] = -lowerBounds[i];
58
- bounds[(i + 1) * dim + 0] = upperBounds[i];
59
- }
60
- return new _DBM(bounds, dim, clockNames, false).canonicalize();
61
- }
62
- /** Creates an empty (unsatisfiable) zone. */
63
- static empty(clockNames) {
64
- const b = new Float64Array(1);
65
- b[0] = 0;
66
- return new _DBM(b, 1, clockNames, true);
67
- }
68
- isEmpty() {
69
- return this._empty;
70
- }
71
- clockCount() {
72
- return this.clockNames.length;
73
- }
74
- get(i, j) {
75
- return this.bounds[i * this.dim + j];
76
- }
77
- /** Gets the lower bound (earliest firing time) for clock i. */
78
- getLowerBound(clockIndex) {
79
- if (this._empty || clockIndex < 0 || clockIndex >= this.clockNames.length) return 0;
80
- const val = -this.get(0, clockIndex + 1);
81
- return val === 0 ? 0 : val;
82
- }
83
- /** Gets the upper bound (latest firing time / deadline) for clock i. */
84
- getUpperBound(clockIndex) {
85
- if (this._empty || clockIndex < 0 || clockIndex >= this.clockNames.length) return Infinity;
86
- return this.get(clockIndex + 1, 0);
87
- }
88
- /** Checks if transition can fire (lower bound <= 0 after time passage). */
89
- canFire(clockIndex) {
90
- return !this._empty && this.getLowerBound(clockIndex) <= EPSILON;
91
- }
92
- /**
93
- * Computes the successor firing domain after firing transition t_f.
94
- * Implements the 5-step Berthomieu-Diaz successor formula.
95
- */
96
- fireTransition(firedClock, newClockNames, newLowerBounds, newUpperBounds, persistentClocks) {
97
- if (this._empty) return this;
98
- const n = this.clockNames.length;
99
- if (firedClock < 0 || firedClock >= n) {
100
- throw new Error(`Invalid fired clock index: ${firedClock}`);
101
- }
102
- const constrained = new Float64Array(this.bounds);
103
- const dim = this.dim;
104
- const f = firedClock + 1;
105
- for (let i = 0; i < n; i++) {
106
- if (i !== firedClock) {
107
- const idx = i + 1;
108
- const pos = f * dim + idx;
109
- constrained[pos] = Math.min(constrained[pos], 0);
110
- }
111
- }
112
- if (!canonicalizeInPlace(constrained, dim)) {
113
- return _DBM.empty([]);
114
- }
115
- const newN = persistentClocks.length + newClockNames.length;
116
- const newDim = newN + 1;
117
- const newBounds = makeMatrix(newDim, Infinity);
118
- for (let pi = 0; pi < persistentClocks.length; pi++) {
119
- const oldIdx = persistentClocks[pi] + 1;
120
- const newIdx = pi + 1;
121
- const upper = constrained[oldIdx * dim + f];
122
- const lower = Math.max(0, -constrained[f * dim + oldIdx]);
123
- newBounds[0 * newDim + newIdx] = -lower;
124
- newBounds[newIdx * newDim + 0] = upper;
125
- for (let pj = 0; pj < persistentClocks.length; pj++) {
126
- const oldJ = persistentClocks[pj] + 1;
127
- const newJ = pj + 1;
128
- newBounds[newIdx * newDim + newJ] = constrained[oldIdx * dim + oldJ];
129
- }
130
- }
131
- const offset = persistentClocks.length;
132
- for (let k = 0; k < newClockNames.length; k++) {
133
- const idx = offset + k + 1;
134
- newBounds[0 * newDim + idx] = -newLowerBounds[k];
135
- newBounds[idx * newDim + 0] = newUpperBounds[k];
136
- }
137
- const allNames = [];
138
- for (const idx of persistentClocks) {
139
- allNames.push(this.clockNames[idx]);
140
- }
141
- allNames.push(...newClockNames);
142
- return new _DBM(newBounds, newDim, allNames, false).canonicalize();
143
- }
144
- /** Lets time pass: set all lower bounds to 0. */
145
- letTimePass() {
146
- if (this._empty) return this;
147
- const newBounds = new Float64Array(this.bounds);
148
- for (let i = 1; i < this.dim; i++) {
149
- newBounds[0 * this.dim + i] = 0;
150
- }
151
- return new _DBM(newBounds, this.dim, this.clockNames, false).canonicalize();
152
- }
153
- canonicalize() {
154
- if (this._empty) return this;
155
- const canon = new Float64Array(this.bounds);
156
- if (!canonicalizeInPlace(canon, this.dim)) {
157
- return _DBM.empty(this.clockNames);
158
- }
159
- return new _DBM(canon, this.dim, this.clockNames, false);
160
- }
161
- equals(other) {
162
- if (this === other) return true;
163
- if (this._empty && other._empty) return true;
164
- if (this._empty || other._empty) return false;
165
- if (this.clockNames.length !== other.clockNames.length) return false;
166
- for (let i = 0; i < this.clockNames.length; i++) {
167
- if (this.clockNames[i] !== other.clockNames[i]) return false;
168
- }
169
- if (this.bounds.length !== other.bounds.length) return false;
170
- for (let i = 0; i < this.bounds.length; i++) {
171
- if (Math.abs(this.bounds[i] - other.bounds[i]) > EPSILON) return false;
172
- }
173
- return true;
174
- }
175
- toString() {
176
- if (this._empty) return "DBM[empty]";
177
- const parts = [];
178
- for (let i = 0; i < this.clockNames.length; i++) {
179
- const lo = formatBound(this.getLowerBound(i));
180
- const hi = formatBound(this.getUpperBound(i));
181
- parts.push(`${this.clockNames[i]}:[${lo},${hi}]`);
182
- }
183
- return `DBM{${parts.join(", ")}}`;
184
- }
185
- };
186
- function makeMatrix(dim, fill) {
187
- const m = new Float64Array(dim * dim).fill(fill);
188
- for (let i = 0; i < dim; i++) {
189
- m[i * dim + i] = 0;
190
- }
191
- return m;
192
- }
193
- function canonicalizeInPlace(dbm, dim) {
194
- for (let k = 0; k < dim; k++) {
195
- for (let i = 0; i < dim; i++) {
196
- for (let j = 0; j < dim; j++) {
197
- const ik = dbm[i * dim + k];
198
- const kj = dbm[k * dim + j];
199
- if (ik < Infinity && kj < Infinity) {
200
- const via = ik + kj;
201
- if (via < dbm[i * dim + j]) {
202
- dbm[i * dim + j] = via;
203
- }
204
- }
205
- }
206
- }
207
- }
208
- for (let i = 0; i < dim; i++) {
209
- if (dbm[i * dim + i] < -EPSILON) return false;
210
- }
211
- return true;
212
- }
213
- function formatBound(b) {
214
- if (b >= Infinity / 2) return "\u221E";
215
- if (b === Math.trunc(b)) return String(b);
216
- return b.toFixed(3);
217
- }
218
-
219
- // src/verification/analysis/state-class.ts
220
- var StateClass = class {
221
- marking;
222
- firingDomain;
223
- enabledTransitions;
224
- constructor(marking, firingDomain, enabledTransitions) {
225
- this.marking = marking;
226
- this.firingDomain = firingDomain;
227
- this.enabledTransitions = [...enabledTransitions];
228
- }
229
- isEmpty() {
230
- return this.firingDomain.isEmpty();
231
- }
232
- canFire(transition) {
233
- const idx = this.enabledTransitions.indexOf(transition);
234
- if (idx < 0) return false;
235
- return this.firingDomain.getUpperBound(idx) >= 0;
236
- }
237
- transitionIndex(transition) {
238
- return this.enabledTransitions.indexOf(transition);
239
- }
240
- equals(other) {
241
- if (this === other) return true;
242
- return this.marking.toString() === other.marking.toString() && this.firingDomain.equals(other.firingDomain);
243
- }
244
- toString() {
245
- return `StateClass{${this.marking}, ${this.firingDomain}}`;
246
- }
247
- };
37
+ } from "../chunk-M3KT7BFO.js";
38
+ import "../chunk-ATT7U5H5.js";
248
39
 
249
40
  // src/verification/analysis/scc-analyzer.ts
250
41
  function computeSCCs(nodes, successors) {
@@ -308,298 +99,6 @@ function findTerminalSCCs(nodes, successors) {
308
99
  return terminal;
309
100
  }
310
101
 
311
- // src/verification/analysis/state-class-graph.ts
312
- var StateClassGraph = class _StateClassGraph {
313
- net;
314
- initialClass;
315
- _stateClasses;
316
- _transitions;
317
- _successors;
318
- _predecessors;
319
- _complete;
320
- constructor(net, initialClass, stateClasses, transitions, complete) {
321
- this.net = net;
322
- this.initialClass = initialClass;
323
- this._stateClasses = stateClasses;
324
- this._transitions = transitions;
325
- this._complete = complete;
326
- this._successors = /* @__PURE__ */ new Map();
327
- this._predecessors = /* @__PURE__ */ new Map();
328
- for (const sc of stateClasses) {
329
- this._successors.set(sc, /* @__PURE__ */ new Set());
330
- this._predecessors.set(sc, /* @__PURE__ */ new Set());
331
- }
332
- for (const [from, tMap] of transitions) {
333
- for (const edges of tMap.values()) {
334
- for (const edge of edges) {
335
- this._successors.get(from).add(edge.target);
336
- this._predecessors.get(edge.target).add(from);
337
- }
338
- }
339
- }
340
- }
341
- /** Builds the state class graph for a Time Petri Net. */
342
- static build(net, initialMarking, maxClasses, environmentPlaces, environmentMode) {
343
- const envMode = environmentMode ?? ignore();
344
- const envPlaces = /* @__PURE__ */ new Set();
345
- if (environmentPlaces) {
346
- for (const ep of environmentPlaces) {
347
- envPlaces.add(ep.place);
348
- }
349
- }
350
- const enabledTransitions = findEnabledTransitions(net, initialMarking, envPlaces, envMode);
351
- const clockNames = enabledTransitions.map((t) => t.name);
352
- const lowerBounds = enabledTransitions.map((t) => earliest(t.timing) / 1e3);
353
- const upperBounds = enabledTransitions.map((t) => latest(t.timing) / 1e3);
354
- let initialDBM = DBM.create(clockNames, lowerBounds, upperBounds);
355
- initialDBM = initialDBM.letTimePass();
356
- const initialClass = new StateClass(initialMarking, initialDBM, enabledTransitions);
357
- const stateClasses = [initialClass];
358
- const stateClassSet = /* @__PURE__ */ new Set([classKey(initialClass)]);
359
- const classMap = /* @__PURE__ */ new Map([[classKey(initialClass), initialClass]]);
360
- const transitionMap = /* @__PURE__ */ new Map();
361
- transitionMap.set(initialClass, /* @__PURE__ */ new Map());
362
- const queue = [initialClass];
363
- let complete = true;
364
- while (queue.length > 0) {
365
- if (stateClasses.length >= maxClasses) {
366
- complete = false;
367
- break;
368
- }
369
- const current = queue.shift();
370
- for (const transition of current.enabledTransitions) {
371
- const virtualTransitions = expandTransition(transition);
372
- for (const vt of virtualTransitions) {
373
- const successor = computeSuccessor(net, current, vt, envPlaces, envMode);
374
- if (successor === null || successor.isEmpty()) continue;
375
- const tEdges = transitionMap.get(current);
376
- if (!tEdges.has(transition)) tEdges.set(transition, []);
377
- tEdges.get(transition).push({ branchIndex: vt.branchIndex, target: successor });
378
- const key = classKey(successor);
379
- if (!stateClassSet.has(key)) {
380
- stateClassSet.add(key);
381
- classMap.set(key, successor);
382
- stateClasses.push(successor);
383
- transitionMap.set(successor, /* @__PURE__ */ new Map());
384
- queue.push(successor);
385
- } else {
386
- const canonical = classMap.get(key);
387
- if (canonical !== successor) {
388
- const edges = tEdges.get(transition);
389
- edges[edges.length - 1] = { branchIndex: vt.branchIndex, target: canonical };
390
- }
391
- }
392
- }
393
- }
394
- }
395
- return new _StateClassGraph(net, initialClass, stateClasses, transitionMap, complete);
396
- }
397
- stateClasses() {
398
- return this._stateClasses;
399
- }
400
- size() {
401
- return this._stateClasses.length;
402
- }
403
- isComplete() {
404
- return this._complete;
405
- }
406
- successors(sc) {
407
- return this._successors.get(sc) ?? /* @__PURE__ */ new Set();
408
- }
409
- predecessors(sc) {
410
- return this._predecessors.get(sc) ?? /* @__PURE__ */ new Set();
411
- }
412
- /** Returns all outgoing transitions with their branch edges. */
413
- outgoingBranchEdges(sc) {
414
- return this._transitions.get(sc) ?? /* @__PURE__ */ new Map();
415
- }
416
- /** Returns the branch edges for a specific transition from a state class. */
417
- branchEdges(sc, transition) {
418
- const map = this._transitions.get(sc);
419
- if (!map) return [];
420
- return map.get(transition) ?? [];
421
- }
422
- /** Returns all transitions that are enabled from a state class. */
423
- enabledTransitions(sc) {
424
- const map = this._transitions.get(sc);
425
- if (!map) return /* @__PURE__ */ new Set();
426
- return new Set(map.keys());
427
- }
428
- /** Finds all state classes with a given marking. */
429
- classesWithMarking(marking) {
430
- const key = marking.toString();
431
- return this._stateClasses.filter((sc) => sc.marking.toString() === key);
432
- }
433
- /** Checks if a marking is reachable. */
434
- isReachable(marking) {
435
- const key = marking.toString();
436
- return this._stateClasses.some((sc) => sc.marking.toString() === key);
437
- }
438
- /** Gets all reachable markings. */
439
- reachableMarkings() {
440
- const markings = /* @__PURE__ */ new Set();
441
- for (const sc of this._stateClasses) {
442
- markings.add(sc.marking.toString());
443
- }
444
- return markings;
445
- }
446
- /** Counts edges in the graph (each branch edge counts separately). */
447
- edgeCount() {
448
- let count = 0;
449
- for (const map of this._transitions.values()) {
450
- for (const edges of map.values()) {
451
- count += edges.length;
452
- }
453
- }
454
- return count;
455
- }
456
- toString() {
457
- return `StateClassGraph[classes=${this.size()}, edges=${this.edgeCount()}, complete=${this._complete}]`;
458
- }
459
- };
460
- function classKey(sc) {
461
- return `${sc.marking.toString()}|${sc.firingDomain.toString()}`;
462
- }
463
- function expandTransition(t) {
464
- let branches;
465
- if (t.outputSpec !== null) {
466
- branches = enumerateBranches(t.outputSpec);
467
- } else {
468
- branches = [/* @__PURE__ */ new Set()];
469
- }
470
- return branches.map((outputPlaces, i) => ({
471
- transition: t,
472
- branchIndex: i,
473
- outputPlaces
474
- }));
475
- }
476
- function computeSuccessor(net, current, fired, environmentPlaces, environmentMode) {
477
- const transition = fired.transition;
478
- const newMarking = fireTransition(current.marking, transition, fired.outputPlaces, environmentPlaces, environmentMode);
479
- const newEnabledAll = findEnabledTransitions(net, newMarking, environmentPlaces, environmentMode);
480
- const persistent = [];
481
- const persistentIndices = [];
482
- for (let i = 0; i < current.enabledTransitions.length; i++) {
483
- const t = current.enabledTransitions[i];
484
- if (t !== transition && newEnabledAll.includes(t)) {
485
- persistent.push(t);
486
- persistentIndices.push(i);
487
- }
488
- }
489
- const newlyEnabled = [];
490
- for (const t of newEnabledAll) {
491
- if (!persistent.includes(t)) {
492
- newlyEnabled.push(t);
493
- }
494
- }
495
- const firedIdx = current.transitionIndex(transition);
496
- const newClockNames = newlyEnabled.map((t) => t.name);
497
- const newLowerBounds = newlyEnabled.map((t) => earliest(t.timing) / 1e3);
498
- const newUpperBounds = newlyEnabled.map((t) => latest(t.timing) / 1e3);
499
- let newDBM = current.firingDomain.fireTransition(
500
- firedIdx,
501
- newClockNames,
502
- newLowerBounds,
503
- newUpperBounds,
504
- persistentIndices
505
- );
506
- newDBM = newDBM.letTimePass();
507
- const allEnabled = [...persistent, ...newlyEnabled];
508
- return new StateClass(newMarking, newDBM, allEnabled);
509
- }
510
- function findEnabledTransitions(net, marking, environmentPlaces, environmentMode) {
511
- const enabled = [];
512
- for (const transition of net.transitions) {
513
- if (isEnabled(transition, marking, environmentPlaces, environmentMode)) {
514
- enabled.push(transition);
515
- }
516
- }
517
- return enabled;
518
- }
519
- function isEnabled(transition, marking, environmentPlaces, environmentMode) {
520
- for (const spec of transition.inputSpecs) {
521
- const required = inputRequiredCount(spec);
522
- if (!checkPlaceEnabled(spec.place, required, marking, environmentPlaces, environmentMode)) {
523
- return false;
524
- }
525
- }
526
- for (const arc of transition.reads) {
527
- if (!checkPlaceEnabled(arc.place, 1, marking, environmentPlaces, environmentMode)) {
528
- return false;
529
- }
530
- }
531
- for (const arc of transition.inhibitors) {
532
- if (marking.hasTokens(arc.place)) {
533
- return false;
534
- }
535
- }
536
- return true;
537
- }
538
- function inputRequiredCount(spec) {
539
- switch (spec.type) {
540
- case "one":
541
- return 1;
542
- case "exactly":
543
- return spec.count;
544
- case "all":
545
- return 1;
546
- case "at-least":
547
- return spec.minimum;
548
- }
549
- }
550
- function inputConsumeCount(spec) {
551
- switch (spec.type) {
552
- case "one":
553
- return 1;
554
- case "exactly":
555
- return spec.count;
556
- case "all":
557
- return 1;
558
- // Analysis: consume minimum (1 token)
559
- case "at-least":
560
- return spec.minimum;
561
- }
562
- }
563
- function checkPlaceEnabled(place, required, marking, environmentPlaces, environmentMode) {
564
- if (!environmentPlaces.has(place)) {
565
- return marking.tokens(place) >= required;
566
- }
567
- switch (environmentMode.type) {
568
- case "always-available":
569
- return true;
570
- case "bounded":
571
- return required <= environmentMode.maxTokens;
572
- case "ignore":
573
- return marking.tokens(place) >= required;
574
- }
575
- }
576
- function fireTransition(marking, transition, outputPlaces, environmentPlaces, environmentMode) {
577
- const builder = MarkingState.builder().copyFrom(marking);
578
- for (const spec of transition.inputSpecs) {
579
- const toConsume = inputConsumeCount(spec);
580
- consumeFromPlace(builder, spec.place, toConsume, environmentPlaces, environmentMode);
581
- }
582
- for (const arc of transition.resets) {
583
- const current = marking.tokens(arc.place);
584
- if (current > 0) {
585
- builder.removeTokens(arc.place, current);
586
- }
587
- }
588
- for (const place of outputPlaces) {
589
- builder.addTokens(place, 1);
590
- }
591
- return builder.build();
592
- }
593
- function consumeFromPlace(builder, place, count, environmentPlaces, environmentMode) {
594
- if (!environmentPlaces.has(place)) {
595
- builder.removeTokens(place, count);
596
- return;
597
- }
598
- if (environmentMode.type === "ignore") {
599
- builder.removeTokens(place, count);
600
- }
601
- }
602
-
603
102
  // src/verification/analysis/time-petri-net-analyzer.ts
604
103
  var TimePetriNetAnalyzer = class _TimePetriNetAnalyzer {
605
104
  net;
@@ -900,6 +399,7 @@ export {
900
399
  bounded as analysisBounded,
901
400
  ignore as analysisIgnore,
902
401
  bounded,
402
+ branchPlaceBound,
903
403
  computePInvariants,
904
404
  computeSCCs,
905
405
  createSpacerRunner,
@@ -918,6 +418,7 @@ export {
918
418
  isCoveredByInvariants,
919
419
  isProven,
920
420
  isViolated,
421
+ joinedOrDeadLettered,
921
422
  mutualExclusion,
922
423
  pInvariant,
923
424
  pInvariantToString,