libpetri 1.8.4 → 2.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.
Files changed (41) hide show
  1. package/README.md +47 -0
  2. package/dist/{chunk-B2D5DMTO.js → chunk-4L6JVKH4.js} +165 -8
  3. package/dist/chunk-4L6JVKH4.js.map +1 -0
  4. package/dist/chunk-H62Z76FY.js +1346 -0
  5. package/dist/chunk-H62Z76FY.js.map +1 -0
  6. package/dist/chunk-SXK2Z45Z.js +50 -0
  7. package/dist/chunk-SXK2Z45Z.js.map +1 -0
  8. package/dist/debug/index.d.ts +50 -3
  9. package/dist/debug/index.js +64 -6
  10. package/dist/debug/index.js.map +1 -1
  11. package/dist/doclet/index.d.ts +152 -31
  12. package/dist/doclet/index.js +458 -57
  13. package/dist/doclet/index.js.map +1 -1
  14. package/dist/doclet/resources/petrinet-diagrams.css +384 -7
  15. package/dist/doclet/resources/petrinet-diagrams.js +7214 -106
  16. package/dist/dot-exporter-PMHOQHZ3.js +8 -0
  17. package/dist/{event-store-BnyHh3TF.d.ts → event-store-2zkXeQkd.d.ts} +1 -1
  18. package/dist/export/index.d.ts +16 -2
  19. package/dist/export/index.js +1 -1
  20. package/dist/index.d.ts +41 -5
  21. package/dist/index.js +1221 -35
  22. package/dist/index.js.map +1 -1
  23. package/dist/petri-net-BDrj4XZE.d.ts +1461 -0
  24. package/dist/render-P6GROU7J.js +16 -0
  25. package/dist/render-P6GROU7J.js.map +1 -0
  26. package/dist/verification/index.d.ts +3 -144
  27. package/dist/verification/index.js +30 -1214
  28. package/dist/verification/index.js.map +1 -1
  29. package/dist/viewer/index.d.ts +196 -0
  30. package/dist/viewer/index.js +442 -0
  31. package/dist/viewer/index.js.map +1 -0
  32. package/dist/viewer/viewer-static.iife.js +3 -0
  33. package/dist/viewer/viewer.css +503 -0
  34. package/dist/viewer/viewer.iife.js +7219 -0
  35. package/package.json +17 -8
  36. package/dist/chunk-B2D5DMTO.js.map +0 -1
  37. package/dist/chunk-VQ4XMJTD.js +0 -107
  38. package/dist/chunk-VQ4XMJTD.js.map +0 -1
  39. package/dist/dot-exporter-3CVCH6J4.js +0 -8
  40. package/dist/petri-net-D-GN9g_D.d.ts +0 -570
  41. /package/dist/{dot-exporter-3CVCH6J4.js.map → dot-exporter-PMHOQHZ3.js.map} +0 -0
@@ -0,0 +1,1346 @@
1
+ // src/core/out.ts
2
+ function and(...children) {
3
+ if (children.length === 0) {
4
+ throw new Error("AND requires at least 1 child");
5
+ }
6
+ return { type: "and", children };
7
+ }
8
+ function andPlaces(...places) {
9
+ return and(...places.map(outPlace));
10
+ }
11
+ function xor(...children) {
12
+ if (children.length < 2) {
13
+ throw new Error("XOR requires at least 2 children");
14
+ }
15
+ return { type: "xor", children };
16
+ }
17
+ function xorPlaces(...places) {
18
+ return xor(...places.map(outPlace));
19
+ }
20
+ function outPlace(p) {
21
+ return { type: "place", place: p };
22
+ }
23
+ function timeout(afterMs, child) {
24
+ if (afterMs <= 0) {
25
+ throw new Error(`Timeout must be positive: ${afterMs}`);
26
+ }
27
+ return { type: "timeout", afterMs, child };
28
+ }
29
+ function timeoutPlace(afterMs, p) {
30
+ return timeout(afterMs, outPlace(p));
31
+ }
32
+ function forwardInput(from, to) {
33
+ return { type: "forward-input", from, to };
34
+ }
35
+ function allPlaces(out) {
36
+ const result = /* @__PURE__ */ new Set();
37
+ collectPlaces(out, result);
38
+ return result;
39
+ }
40
+ function collectPlaces(out, result) {
41
+ switch (out.type) {
42
+ case "place":
43
+ result.add(out.place);
44
+ break;
45
+ case "forward-input":
46
+ result.add(out.to);
47
+ break;
48
+ case "and":
49
+ case "xor":
50
+ for (const child of out.children) {
51
+ collectPlaces(child, result);
52
+ }
53
+ break;
54
+ case "timeout":
55
+ collectPlaces(out.child, result);
56
+ break;
57
+ }
58
+ }
59
+ function enumerateBranches(out) {
60
+ switch (out.type) {
61
+ case "place":
62
+ return [/* @__PURE__ */ new Set([out.place])];
63
+ case "forward-input":
64
+ return [/* @__PURE__ */ new Set([out.to])];
65
+ case "and": {
66
+ let result = [/* @__PURE__ */ new Set()];
67
+ for (const child of out.children) {
68
+ result = crossProduct(result, enumerateBranches(child));
69
+ }
70
+ return result;
71
+ }
72
+ case "xor": {
73
+ const result = [];
74
+ for (const child of out.children) {
75
+ result.push(...enumerateBranches(child));
76
+ }
77
+ return result;
78
+ }
79
+ case "timeout":
80
+ return enumerateBranches(out.child);
81
+ }
82
+ }
83
+ function crossProduct(a, b) {
84
+ const result = [];
85
+ for (const setA of a) {
86
+ for (const setB of b) {
87
+ const merged = new Set(setA);
88
+ for (const p of setB) merged.add(p);
89
+ result.push(merged);
90
+ }
91
+ }
92
+ return result;
93
+ }
94
+
95
+ // src/verification/marking-state.ts
96
+ var MARKING_STATE_KEY = /* @__PURE__ */ Symbol("MarkingState.internal");
97
+ var MarkingState = class _MarkingState {
98
+ tokenCounts;
99
+ placesByName;
100
+ /** @internal Use {@link MarkingState.builder} or {@link MarkingState.empty} to create instances. */
101
+ constructor(key, tokenCounts, placesByName) {
102
+ if (key !== MARKING_STATE_KEY) throw new Error("Use MarkingState.builder() to create instances");
103
+ this.tokenCounts = tokenCounts;
104
+ this.placesByName = placesByName;
105
+ }
106
+ /** Returns the token count for a place (0 if absent). */
107
+ tokens(place) {
108
+ return this.tokenCounts.get(place.name) ?? 0;
109
+ }
110
+ /** Checks if a place has at least one token. */
111
+ hasTokens(place) {
112
+ return this.tokens(place) > 0;
113
+ }
114
+ /** Checks if any of the given places has tokens. */
115
+ hasTokensInAny(places) {
116
+ for (const p of places) {
117
+ if (this.hasTokens(p)) return true;
118
+ }
119
+ return false;
120
+ }
121
+ /** Returns all places with tokens > 0. */
122
+ placesWithTokens() {
123
+ return [...this.placesByName.values()];
124
+ }
125
+ /** Returns the total number of tokens. */
126
+ totalTokens() {
127
+ let sum = 0;
128
+ for (const count of this.tokenCounts.values()) sum += count;
129
+ return sum;
130
+ }
131
+ /** Checks if no tokens exist anywhere. */
132
+ isEmpty() {
133
+ return this.tokenCounts.size === 0;
134
+ }
135
+ toString() {
136
+ if (this.tokenCounts.size === 0) return "{}";
137
+ const entries = [...this.tokenCounts.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([name, count]) => `${name}:${count}`);
138
+ return `{${entries.join(", ")}}`;
139
+ }
140
+ static empty() {
141
+ return new _MarkingState(MARKING_STATE_KEY, /* @__PURE__ */ new Map(), /* @__PURE__ */ new Map());
142
+ }
143
+ static builder() {
144
+ return new MarkingStateBuilder();
145
+ }
146
+ };
147
+ var MarkingStateBuilder = class {
148
+ tokenCounts = /* @__PURE__ */ new Map();
149
+ placesByName = /* @__PURE__ */ new Map();
150
+ /** Sets the token count for a place. */
151
+ tokens(place, count) {
152
+ if (count < 0) throw new Error(`Token count cannot be negative: ${count}`);
153
+ if (count > 0) {
154
+ this.tokenCounts.set(place.name, count);
155
+ this.placesByName.set(place.name, place);
156
+ } else {
157
+ this.tokenCounts.delete(place.name);
158
+ this.placesByName.delete(place.name);
159
+ }
160
+ return this;
161
+ }
162
+ /** Adds tokens to a place. */
163
+ addTokens(place, count) {
164
+ if (count < 0) throw new Error(`Token count cannot be negative: ${count}`);
165
+ if (count > 0) {
166
+ const current = this.tokenCounts.get(place.name) ?? 0;
167
+ this.tokenCounts.set(place.name, current + count);
168
+ this.placesByName.set(place.name, place);
169
+ }
170
+ return this;
171
+ }
172
+ /** Removes tokens from a place. Throws if insufficient. */
173
+ removeTokens(place, count) {
174
+ const current = this.tokenCounts.get(place.name) ?? 0;
175
+ const newCount = current - count;
176
+ if (newCount < 0) {
177
+ throw new Error(
178
+ `Cannot remove ${count} tokens from ${place.name} (has ${current})`
179
+ );
180
+ }
181
+ if (newCount === 0) {
182
+ this.tokenCounts.delete(place.name);
183
+ this.placesByName.delete(place.name);
184
+ } else {
185
+ this.tokenCounts.set(place.name, newCount);
186
+ }
187
+ return this;
188
+ }
189
+ /** Copies all token counts from another marking state. */
190
+ copyFrom(other) {
191
+ for (const p of other.placesWithTokens()) {
192
+ this.tokenCounts.set(p.name, other.tokens(p));
193
+ this.placesByName.set(p.name, p);
194
+ }
195
+ return this;
196
+ }
197
+ build() {
198
+ return new MarkingState(MARKING_STATE_KEY, new Map(this.tokenCounts), new Map(this.placesByName));
199
+ }
200
+ };
201
+
202
+ // src/verification/smt-property.ts
203
+ function deadlockFree() {
204
+ return { type: "deadlock-free" };
205
+ }
206
+ function mutualExclusion(p1, p2) {
207
+ return { type: "mutual-exclusion", p1, p2 };
208
+ }
209
+ function placeBound(place, bound) {
210
+ return { type: "place-bound", place, bound };
211
+ }
212
+ function unreachable(places) {
213
+ return { type: "unreachable", places: new Set(places) };
214
+ }
215
+ function propertyDescription(prop) {
216
+ switch (prop.type) {
217
+ case "deadlock-free":
218
+ return "Deadlock-freedom";
219
+ case "mutual-exclusion":
220
+ return `Mutual exclusion of ${prop.p1.name} and ${prop.p2.name}`;
221
+ case "place-bound":
222
+ return `Place ${prop.place.name} bounded by ${prop.bound}`;
223
+ case "unreachable":
224
+ return `Unreachability of marking with tokens in {${[...prop.places].map((p) => p.name).join(", ")}}`;
225
+ }
226
+ }
227
+
228
+ // src/verification/encoding/flat-transition.ts
229
+ function flatTransition(name, source, branchIndex, preVector, postVector, inhibitorPlaces, readPlaces, resetPlaces, consumeAll) {
230
+ return {
231
+ name,
232
+ source,
233
+ branchIndex,
234
+ preVector,
235
+ postVector,
236
+ inhibitorPlaces,
237
+ readPlaces,
238
+ resetPlaces,
239
+ consumeAll
240
+ };
241
+ }
242
+
243
+ // src/verification/encoding/net-flattener.ts
244
+ function unbounded() {
245
+ return { type: "unbounded" };
246
+ }
247
+ function bounded(maxTokens) {
248
+ return { type: "bounded", maxTokens };
249
+ }
250
+ function flatten(net, environmentPlaces = /* @__PURE__ */ new Set(), environmentMode = unbounded()) {
251
+ const allPlacesSet = /* @__PURE__ */ new Map();
252
+ for (const p of net.places) {
253
+ allPlacesSet.set(p.name, p);
254
+ }
255
+ for (const t of net.transitions) {
256
+ for (const inSpec of t.inputSpecs) {
257
+ allPlacesSet.set(inSpec.place.name, inSpec.place);
258
+ }
259
+ if (t.outputSpec !== null) {
260
+ for (const p of allPlaces(t.outputSpec)) {
261
+ allPlacesSet.set(p.name, p);
262
+ }
263
+ }
264
+ for (const arc of t.inhibitors) allPlacesSet.set(arc.place.name, arc.place);
265
+ for (const arc of t.reads) allPlacesSet.set(arc.place.name, arc.place);
266
+ for (const arc of t.resets) allPlacesSet.set(arc.place.name, arc.place);
267
+ }
268
+ const places = [...allPlacesSet.values()].sort((a, b) => a.name.localeCompare(b.name));
269
+ const placeIndex = /* @__PURE__ */ new Map();
270
+ for (let i = 0; i < places.length; i++) {
271
+ placeIndex.set(places[i].name, i);
272
+ }
273
+ const environmentBounds = /* @__PURE__ */ new Map();
274
+ if (environmentMode.type === "bounded") {
275
+ for (const ep of environmentPlaces) {
276
+ environmentBounds.set(ep.place.name, environmentMode.maxTokens);
277
+ }
278
+ }
279
+ const n = places.length;
280
+ const flatTransitions = [];
281
+ for (const transition of net.transitions) {
282
+ const branches = enumerateOutputBranches(transition);
283
+ for (let branchIdx = 0; branchIdx < branches.length; branchIdx++) {
284
+ const branchPlaces = branches[branchIdx];
285
+ const name = branches.length > 1 ? `${transition.name}_b${branchIdx}` : transition.name;
286
+ const preVector = new Array(n).fill(0);
287
+ const consumeAll = new Array(n).fill(false);
288
+ for (const inSpec of transition.inputSpecs) {
289
+ const idx = placeIndex.get(inSpec.place.name);
290
+ if (idx === void 0) continue;
291
+ switch (inSpec.type) {
292
+ case "one":
293
+ preVector[idx] = 1;
294
+ break;
295
+ case "exactly":
296
+ preVector[idx] = inSpec.count;
297
+ break;
298
+ case "all":
299
+ preVector[idx] = 1;
300
+ consumeAll[idx] = true;
301
+ break;
302
+ case "at-least":
303
+ preVector[idx] = inSpec.minimum;
304
+ consumeAll[idx] = true;
305
+ break;
306
+ }
307
+ }
308
+ const postVector = new Array(n).fill(0);
309
+ for (const p of branchPlaces) {
310
+ const idx = placeIndex.get(p.name);
311
+ if (idx !== void 0) {
312
+ postVector[idx] = 1;
313
+ }
314
+ }
315
+ const inhibitorPlaces = transition.inhibitors.map((arc) => placeIndex.get(arc.place.name)).filter((idx) => idx !== void 0);
316
+ const readPlaces = transition.reads.map((arc) => placeIndex.get(arc.place.name)).filter((idx) => idx !== void 0);
317
+ const resetPlaces = transition.resets.map((arc) => placeIndex.get(arc.place.name)).filter((idx) => idx !== void 0);
318
+ flatTransitions.push(flatTransition(
319
+ name,
320
+ transition,
321
+ branches.length > 1 ? branchIdx : -1,
322
+ preVector,
323
+ postVector,
324
+ inhibitorPlaces,
325
+ readPlaces,
326
+ resetPlaces,
327
+ consumeAll
328
+ ));
329
+ }
330
+ }
331
+ return {
332
+ places,
333
+ placeIndex,
334
+ transitions: flatTransitions,
335
+ environmentBounds
336
+ };
337
+ }
338
+ function enumerateOutputBranches(t) {
339
+ if (t.outputSpec !== null) {
340
+ return enumerateBranches(t.outputSpec);
341
+ }
342
+ return [/* @__PURE__ */ new Set()];
343
+ }
344
+
345
+ // src/verification/encoding/incidence-matrix.ts
346
+ var IncidenceMatrix = class _IncidenceMatrix {
347
+ _pre;
348
+ _post;
349
+ _incidence;
350
+ _numTransitions;
351
+ _numPlaces;
352
+ constructor(pre, post, incidence, numTransitions, numPlaces) {
353
+ this._pre = pre;
354
+ this._post = post;
355
+ this._incidence = incidence;
356
+ this._numTransitions = numTransitions;
357
+ this._numPlaces = numPlaces;
358
+ }
359
+ /**
360
+ * Computes the incidence matrix from a FlatNet.
361
+ */
362
+ static from(flatNet) {
363
+ const T = flatNet.transitions.length;
364
+ const P = flatNet.places.length;
365
+ const pre = [];
366
+ const post = [];
367
+ const incidence = [];
368
+ for (let t = 0; t < T; t++) {
369
+ const ft = flatNet.transitions[t];
370
+ const preRow = new Array(P);
371
+ const postRow = new Array(P);
372
+ const incRow = new Array(P);
373
+ for (let p = 0; p < P; p++) {
374
+ preRow[p] = ft.preVector[p];
375
+ postRow[p] = ft.postVector[p];
376
+ incRow[p] = postRow[p] - preRow[p];
377
+ }
378
+ pre.push(preRow);
379
+ post.push(postRow);
380
+ incidence.push(incRow);
381
+ }
382
+ return new _IncidenceMatrix(pre, post, incidence, T, P);
383
+ }
384
+ /**
385
+ * Returns C^T (transpose of incidence matrix), dimensions [P][T].
386
+ * Used for P-invariant computation: null space of C^T gives P-invariants.
387
+ */
388
+ transposedIncidence() {
389
+ const ct = [];
390
+ for (let p = 0; p < this._numPlaces; p++) {
391
+ const row = new Array(this._numTransitions);
392
+ for (let t = 0; t < this._numTransitions; t++) {
393
+ row[t] = this._incidence[t][p];
394
+ }
395
+ ct.push(row);
396
+ }
397
+ return ct;
398
+ }
399
+ /** Returns the pre-matrix (tokens consumed). T×P. */
400
+ pre() {
401
+ return this._pre;
402
+ }
403
+ /** Returns the post-matrix (tokens produced). T×P. */
404
+ post() {
405
+ return this._post;
406
+ }
407
+ /** Returns the incidence matrix C[t][p] = post - pre. T×P. */
408
+ incidence() {
409
+ return this._incidence;
410
+ }
411
+ numTransitions() {
412
+ return this._numTransitions;
413
+ }
414
+ numPlaces() {
415
+ return this._numPlaces;
416
+ }
417
+ };
418
+
419
+ // src/verification/invariant/p-invariant.ts
420
+ function pInvariant(weights, constant, support) {
421
+ return { weights, constant, support };
422
+ }
423
+ function pInvariantToString(inv) {
424
+ const parts = [];
425
+ for (const i of inv.support) {
426
+ if (inv.weights[i] !== 1) {
427
+ parts.push(`${inv.weights[i]}*p${i}`);
428
+ } else {
429
+ parts.push(`p${i}`);
430
+ }
431
+ }
432
+ return `PInvariant[${parts.join(" + ")} = ${inv.constant}]`;
433
+ }
434
+
435
+ // src/verification/invariant/p-invariant-computer.ts
436
+ function computePInvariants(matrix, flatNet, initialMarking) {
437
+ const P = matrix.numPlaces();
438
+ const T = matrix.numTransitions();
439
+ if (P === 0 || T === 0) return [];
440
+ const ct = matrix.transposedIncidence();
441
+ const cols = T + P;
442
+ const augmented = [];
443
+ for (let i = 0; i < P; i++) {
444
+ const row = new Array(cols).fill(0);
445
+ for (let j = 0; j < T; j++) {
446
+ row[j] = ct[i][j];
447
+ }
448
+ row[T + i] = 1;
449
+ augmented.push(row);
450
+ }
451
+ let pivotRow = 0;
452
+ for (let col = 0; col < T && pivotRow < P; col++) {
453
+ let pivot = -1;
454
+ for (let row = pivotRow; row < P; row++) {
455
+ if (augmented[row][col] !== 0) {
456
+ pivot = row;
457
+ break;
458
+ }
459
+ }
460
+ if (pivot === -1) continue;
461
+ if (pivot !== pivotRow) {
462
+ const tmp = augmented[pivotRow];
463
+ augmented[pivotRow] = augmented[pivot];
464
+ augmented[pivot] = tmp;
465
+ }
466
+ for (let row = 0; row < P; row++) {
467
+ if (row === pivotRow || augmented[row][col] === 0) continue;
468
+ const a = augmented[pivotRow][col];
469
+ const b = augmented[row][col];
470
+ for (let c = 0; c < cols; c++) {
471
+ augmented[row][c] = a * augmented[row][c] - b * augmented[pivotRow][c];
472
+ }
473
+ normalizeRow(augmented[row], cols);
474
+ }
475
+ pivotRow++;
476
+ }
477
+ const invariants = [];
478
+ for (let row = 0; row < P; row++) {
479
+ let isZero = true;
480
+ for (let col = 0; col < T; col++) {
481
+ if (augmented[row][col] !== 0) {
482
+ isZero = false;
483
+ break;
484
+ }
485
+ }
486
+ if (!isZero) continue;
487
+ const weights = new Array(P);
488
+ let allNonNegative = true;
489
+ let hasPositive = false;
490
+ for (let i = 0; i < P; i++) {
491
+ weights[i] = augmented[row][T + i];
492
+ if (weights[i] < 0) {
493
+ allNonNegative = false;
494
+ break;
495
+ }
496
+ if (weights[i] > 0) hasPositive = true;
497
+ }
498
+ if (!allNonNegative) {
499
+ let allNonPositive = true;
500
+ for (let i = 0; i < P; i++) {
501
+ if (augmented[row][T + i] > 0) {
502
+ allNonPositive = false;
503
+ break;
504
+ }
505
+ }
506
+ if (allNonPositive) {
507
+ for (let i = 0; i < P; i++) {
508
+ weights[i] = -augmented[row][T + i];
509
+ }
510
+ hasPositive = true;
511
+ allNonNegative = true;
512
+ }
513
+ }
514
+ if (!allNonNegative || !hasPositive) continue;
515
+ let g = 0;
516
+ for (const w of weights) {
517
+ if (w > 0) g = gcd(g, w);
518
+ }
519
+ if (g > 1) {
520
+ for (let i = 0; i < P; i++) {
521
+ weights[i] = weights[i] / g;
522
+ }
523
+ }
524
+ const support = /* @__PURE__ */ new Set();
525
+ let constant = 0;
526
+ for (let i = 0; i < P; i++) {
527
+ if (weights[i] !== 0) {
528
+ support.add(i);
529
+ const place = flatNet.places[i];
530
+ constant += weights[i] * initialMarking.tokens(place);
531
+ }
532
+ }
533
+ invariants.push(pInvariant(weights, constant, support));
534
+ }
535
+ return invariants;
536
+ }
537
+ function isCoveredByInvariants(invariants, numPlaces) {
538
+ const covered = new Array(numPlaces).fill(false);
539
+ for (const inv of invariants) {
540
+ for (const idx of inv.support) {
541
+ if (idx < numPlaces) covered[idx] = true;
542
+ }
543
+ }
544
+ return covered.every((c) => c);
545
+ }
546
+ function normalizeRow(row, cols) {
547
+ let g = 0;
548
+ for (let c = 0; c < cols; c++) {
549
+ if (row[c] !== 0) {
550
+ g = gcd(g, Math.abs(row[c]));
551
+ }
552
+ }
553
+ if (g > 1) {
554
+ for (let c = 0; c < cols; c++) {
555
+ row[c] = row[c] / g;
556
+ }
557
+ }
558
+ }
559
+ function gcd(a, b) {
560
+ while (b !== 0) {
561
+ const t = b;
562
+ b = a % b;
563
+ a = t;
564
+ }
565
+ return a;
566
+ }
567
+
568
+ // src/verification/invariant/structural-check.ts
569
+ var MAX_PLACES_FOR_SIPHON_ANALYSIS = 50;
570
+ function structuralCheck(flatNet, initialMarking) {
571
+ const P = flatNet.places.length;
572
+ if (P === 0) {
573
+ return { type: "no-potential-deadlock" };
574
+ }
575
+ if (P > MAX_PLACES_FOR_SIPHON_ANALYSIS) {
576
+ return { type: "inconclusive", reason: `Net has ${P} places, siphon enumeration skipped` };
577
+ }
578
+ const siphons = findMinimalSiphons(flatNet);
579
+ if (siphons.length === 0) {
580
+ return { type: "no-potential-deadlock" };
581
+ }
582
+ for (const siphon of siphons) {
583
+ const trap = findMaximalTrapIn(flatNet, siphon);
584
+ if (trap.size === 0 || !isMarked(trap, flatNet, initialMarking)) {
585
+ return { type: "potential-deadlock", siphon };
586
+ }
587
+ }
588
+ return { type: "no-potential-deadlock" };
589
+ }
590
+ function findMinimalSiphons(flatNet) {
591
+ const P = flatNet.places.length;
592
+ const siphons = [];
593
+ const placeAsOutput = [];
594
+ for (let p = 0; p < P; p++) {
595
+ placeAsOutput.push([]);
596
+ }
597
+ for (let t = 0; t < flatNet.transitions.length; t++) {
598
+ const ft = flatNet.transitions[t];
599
+ for (let p = 0; p < P; p++) {
600
+ if (ft.postVector[p] > 0) {
601
+ placeAsOutput[p].push(t);
602
+ }
603
+ }
604
+ }
605
+ for (let startPlace = 0; startPlace < P; startPlace++) {
606
+ const siphon = computeSiphonContaining(startPlace, flatNet, placeAsOutput);
607
+ if (siphon !== null && siphon.size > 0) {
608
+ let isMinimal = true;
609
+ const toRemove = [];
610
+ for (let i = 0; i < siphons.length; i++) {
611
+ const existing = siphons[i];
612
+ if (setsEqual(existing, siphon)) {
613
+ isMinimal = false;
614
+ break;
615
+ }
616
+ if (isSubsetOf(existing, siphon)) {
617
+ isMinimal = false;
618
+ break;
619
+ }
620
+ if (isSubsetOf(siphon, existing)) {
621
+ toRemove.push(i);
622
+ }
623
+ }
624
+ for (let i = toRemove.length - 1; i >= 0; i--) {
625
+ siphons.splice(toRemove[i], 1);
626
+ }
627
+ if (isMinimal) {
628
+ siphons.push(siphon);
629
+ }
630
+ }
631
+ }
632
+ return siphons;
633
+ }
634
+ function computeSiphonContaining(startPlace, flatNet, placeAsOutput) {
635
+ const siphon = /* @__PURE__ */ new Set();
636
+ siphon.add(startPlace);
637
+ let changed = true;
638
+ while (changed) {
639
+ changed = false;
640
+ const snapshot = [...siphon];
641
+ for (const p of snapshot) {
642
+ for (const t of placeAsOutput[p]) {
643
+ const ft = flatNet.transitions[t];
644
+ let hasInputInSiphon = false;
645
+ for (let q = 0; q < flatNet.places.length; q++) {
646
+ if (ft.preVector[q] > 0 && siphon.has(q)) {
647
+ hasInputInSiphon = true;
648
+ break;
649
+ }
650
+ }
651
+ if (!hasInputInSiphon) {
652
+ let added = false;
653
+ for (let q = 0; q < flatNet.places.length; q++) {
654
+ if (ft.preVector[q] > 0) {
655
+ if (!siphon.has(q)) {
656
+ siphon.add(q);
657
+ changed = true;
658
+ }
659
+ added = true;
660
+ break;
661
+ }
662
+ }
663
+ if (!added) {
664
+ return null;
665
+ }
666
+ }
667
+ }
668
+ }
669
+ }
670
+ return siphon;
671
+ }
672
+ function findMaximalTrapIn(flatNet, places) {
673
+ const trap = new Set(places);
674
+ let changed = true;
675
+ while (changed) {
676
+ changed = false;
677
+ const toRemove = [];
678
+ for (const p of trap) {
679
+ let satisfies = true;
680
+ for (let t = 0; t < flatNet.transitions.length; t++) {
681
+ const ft = flatNet.transitions[t];
682
+ if (ft.preVector[p] > 0) {
683
+ let outputsToTrap = false;
684
+ for (const q of trap) {
685
+ if (ft.postVector[q] > 0) {
686
+ outputsToTrap = true;
687
+ break;
688
+ }
689
+ }
690
+ if (!outputsToTrap) {
691
+ satisfies = false;
692
+ break;
693
+ }
694
+ }
695
+ }
696
+ if (!satisfies) {
697
+ toRemove.push(p);
698
+ }
699
+ }
700
+ if (toRemove.length > 0) {
701
+ for (const p of toRemove) trap.delete(p);
702
+ changed = true;
703
+ }
704
+ }
705
+ return trap;
706
+ }
707
+ function isMarked(placeIndices, flatNet, marking) {
708
+ for (const idx of placeIndices) {
709
+ const place = flatNet.places[idx];
710
+ if (marking.tokens(place) > 0) return true;
711
+ }
712
+ return false;
713
+ }
714
+ function setsEqual(a, b) {
715
+ if (a.size !== b.size) return false;
716
+ for (const v of a) {
717
+ if (!b.has(v)) return false;
718
+ }
719
+ return true;
720
+ }
721
+ function isSubsetOf(sub, sup) {
722
+ if (sub.size > sup.size) return false;
723
+ for (const v of sub) {
724
+ if (!sup.has(v)) return false;
725
+ }
726
+ return true;
727
+ }
728
+
729
+ // src/verification/z3/spacer-runner.ts
730
+ import { init } from "z3-solver";
731
+ async function createSpacerRunner(timeoutMs) {
732
+ const { Context } = await init();
733
+ const ctx = new Context("main");
734
+ const fp = new ctx.Fixedpoint();
735
+ fp.set("engine", "spacer");
736
+ if (timeoutMs > 0) {
737
+ fp.set("timeout", Math.min(timeoutMs, 2147483647));
738
+ }
739
+ async function query(errorExpr, reachableDecl) {
740
+ try {
741
+ const status = await fp.query(errorExpr);
742
+ if (status === "unsat") {
743
+ let invariantFormula = null;
744
+ const levelInvariants = [];
745
+ try {
746
+ const answer = fp.getAnswer();
747
+ if (answer != null) {
748
+ invariantFormula = answer.toString();
749
+ }
750
+ } catch {
751
+ }
752
+ if (reachableDecl != null) {
753
+ try {
754
+ const levels = fp.getNumLevels(reachableDecl);
755
+ for (let i = 0; i < levels; i++) {
756
+ const cover = fp.getCoverDelta(i, reachableDecl);
757
+ if (cover != null && !ctx.isTrue(cover)) {
758
+ levelInvariants.push(`Level ${i}: ${cover.toString()}`);
759
+ }
760
+ }
761
+ } catch {
762
+ }
763
+ }
764
+ return { type: "proven", invariantFormula, levelInvariants };
765
+ }
766
+ if (status === "sat") {
767
+ let answer = null;
768
+ try {
769
+ answer = fp.getAnswer();
770
+ } catch {
771
+ }
772
+ return { type: "violated", answer };
773
+ }
774
+ return { type: "unknown", reason: fp.getReasonUnknown() };
775
+ } catch (e) {
776
+ return { type: "unknown", reason: `Z3 exception: ${e.message ?? e}` };
777
+ }
778
+ }
779
+ function dispose() {
780
+ try {
781
+ fp.release();
782
+ } catch {
783
+ }
784
+ }
785
+ return {
786
+ ctx,
787
+ fp,
788
+ query,
789
+ dispose
790
+ };
791
+ }
792
+
793
+ // src/verification/encoding/flat-net.ts
794
+ function flatNetPlaceCount(net) {
795
+ return net.places.length;
796
+ }
797
+ function flatNetTransitionCount(net) {
798
+ return net.transitions.length;
799
+ }
800
+ function flatNetIndexOf(net, place) {
801
+ return net.placeIndex.get(place.name) ?? -1;
802
+ }
803
+
804
+ // src/verification/z3/smt-encoder.ts
805
+ function encode(ctx, fp, flatNet, initialMarking, property, invariants, sinkPlaces = /* @__PURE__ */ new Set()) {
806
+ const P = flatNet.places.length;
807
+ const Int = ctx.Int;
808
+ const Bool_ = ctx.Bool;
809
+ const intSort = Int.sort();
810
+ const boolSort = Bool_.sort();
811
+ const markingSorts = new Array(P).fill(intSort);
812
+ const reachable = ctx.Function.declare("Reachable", ...markingSorts, boolSort);
813
+ fp.registerRelation(reachable);
814
+ const error = ctx.Function.declare("Error", boolSort);
815
+ fp.registerRelation(error);
816
+ const m0Args = [];
817
+ for (let i = 0; i < P; i++) {
818
+ const tokens = initialMarking.tokens(flatNet.places[i]);
819
+ m0Args.push(Int.val(tokens));
820
+ }
821
+ const initFact = reachable.call(...m0Args);
822
+ fp.addRule(initFact, "init");
823
+ for (let t = 0; t < flatNet.transitions.length; t++) {
824
+ const ft = flatNet.transitions[t];
825
+ encodeTransitionRule(ctx, fp, reachable, ft, flatNet, invariants, P);
826
+ }
827
+ encodeErrorRule(ctx, fp, reachable, error, flatNet, property, sinkPlaces, P);
828
+ return {
829
+ errorExpr: error.call(),
830
+ reachableDecl: reachable
831
+ };
832
+ }
833
+ function encodeTransitionRule(ctx, fp, reachable, ft, flatNet, invariants, P) {
834
+ const Int = ctx.Int;
835
+ const mVars = [];
836
+ const mPrimeVars = [];
837
+ for (let i = 0; i < P; i++) {
838
+ mVars.push(Int.const(`m${i}`));
839
+ mPrimeVars.push(Int.const(`mp${i}`));
840
+ }
841
+ const reachBody = reachable.call(...mVars);
842
+ const enabled = encodeEnabled(ctx, ft, flatNet, mVars, P);
843
+ const fireRelation = encodeFire(ctx, ft, flatNet, mVars, mPrimeVars, P);
844
+ let nonNeg = ctx.Bool.val(true);
845
+ for (let i = 0; i < P; i++) {
846
+ nonNeg = ctx.And(nonNeg, mPrimeVars[i].ge(0));
847
+ }
848
+ const invConstraints = encodeInvariantConstraints(ctx, invariants, mPrimeVars, P);
849
+ let envBounds = ctx.Bool.val(true);
850
+ for (const [name, bound] of flatNet.environmentBounds) {
851
+ const idx = flatNet.placeIndex.get(name);
852
+ if (idx != null) {
853
+ envBounds = ctx.And(envBounds, mPrimeVars[idx].le(bound));
854
+ }
855
+ }
856
+ const body = ctx.And(reachBody, enabled, fireRelation, nonNeg, invConstraints, envBounds);
857
+ const head = reachable.call(...mPrimeVars);
858
+ const allVars = [...mVars, ...mPrimeVars];
859
+ const rule = ctx.Implies(body, head);
860
+ const qRule = ctx.ForAll(allVars, rule);
861
+ fp.addRule(qRule, `t_${ft.name}`);
862
+ }
863
+ function encodeEnabled(ctx, ft, _flatNet, mVars, P) {
864
+ let result = ctx.Bool.val(true);
865
+ for (let p = 0; p < P; p++) {
866
+ if (ft.preVector[p] > 0) {
867
+ result = ctx.And(result, mVars[p].ge(ft.preVector[p]));
868
+ }
869
+ }
870
+ for (const p of ft.readPlaces) {
871
+ result = ctx.And(result, mVars[p].ge(1));
872
+ }
873
+ for (const p of ft.inhibitorPlaces) {
874
+ result = ctx.And(result, mVars[p].eq(0));
875
+ }
876
+ for (let p = 0; p < P; p++) {
877
+ result = ctx.And(result, mVars[p].ge(0));
878
+ }
879
+ return result;
880
+ }
881
+ function encodeFire(ctx, ft, _flatNet, mVars, mPrimeVars, P) {
882
+ let result = ctx.Bool.val(true);
883
+ for (let p = 0; p < P; p++) {
884
+ const isReset = ft.resetPlaces.includes(p);
885
+ if (isReset || ft.consumeAll[p]) {
886
+ result = ctx.And(result, mPrimeVars[p].eq(ft.postVector[p]));
887
+ } else {
888
+ const delta = ft.postVector[p] - ft.preVector[p];
889
+ if (delta === 0) {
890
+ result = ctx.And(result, mPrimeVars[p].eq(mVars[p]));
891
+ } else {
892
+ result = ctx.And(result, mPrimeVars[p].eq(mVars[p].add(delta)));
893
+ }
894
+ }
895
+ }
896
+ return result;
897
+ }
898
+ function encodeErrorRule(ctx, fp, reachable, error, flatNet, property, sinkPlaces, P) {
899
+ const Int = ctx.Int;
900
+ const mVars = [];
901
+ for (let i = 0; i < P; i++) {
902
+ mVars.push(Int.const(`em${i}`));
903
+ }
904
+ const reachBody = reachable.call(...mVars);
905
+ const violation = encodePropertyViolation(ctx, flatNet, property, sinkPlaces, mVars, P);
906
+ const head = error.call();
907
+ const body = ctx.And(reachBody, violation);
908
+ const rule = ctx.Implies(body, head);
909
+ const qRule = ctx.ForAll(mVars, rule);
910
+ fp.addRule(qRule, `error_${property.type}`);
911
+ }
912
+ function encodePropertyViolation(ctx, flatNet, property, sinkPlaces, mVars, P) {
913
+ switch (property.type) {
914
+ case "deadlock-free": {
915
+ const deadlock = encodeDeadlock(ctx, flatNet, mVars, P);
916
+ if (sinkPlaces.size > 0) {
917
+ let notAtSink = ctx.Bool.val(true);
918
+ for (const sink of sinkPlaces) {
919
+ const idx = flatNetIndexOf(flatNet, sink);
920
+ if (idx >= 0) {
921
+ notAtSink = ctx.And(notAtSink, mVars[idx].eq(0));
922
+ }
923
+ }
924
+ return ctx.And(deadlock, notAtSink);
925
+ }
926
+ return deadlock;
927
+ }
928
+ case "mutual-exclusion": {
929
+ const idx1 = flatNetIndexOf(flatNet, property.p1);
930
+ const idx2 = flatNetIndexOf(flatNet, property.p2);
931
+ if (idx1 < 0) throw new Error(`MutualExclusion references unknown place: ${property.p1.name}`);
932
+ if (idx2 < 0) throw new Error(`MutualExclusion references unknown place: ${property.p2.name}`);
933
+ return ctx.And(mVars[idx1].ge(1), mVars[idx2].ge(1));
934
+ }
935
+ case "place-bound": {
936
+ const idx = flatNetIndexOf(flatNet, property.place);
937
+ if (idx < 0) throw new Error(`PlaceBound references unknown place: ${property.place.name}`);
938
+ return mVars[idx].gt(property.bound);
939
+ }
940
+ case "unreachable": {
941
+ let allMarked = ctx.Bool.val(true);
942
+ for (const place of property.places) {
943
+ const idx = flatNetIndexOf(flatNet, place);
944
+ if (idx >= 0) {
945
+ allMarked = ctx.And(allMarked, mVars[idx].ge(1));
946
+ }
947
+ }
948
+ return allMarked;
949
+ }
950
+ }
951
+ }
952
+ function encodeDeadlock(ctx, flatNet, mVars, P) {
953
+ let deadlock = ctx.Bool.val(true);
954
+ for (const ft of flatNet.transitions) {
955
+ const enabled = encodeEnabled(ctx, ft, flatNet, mVars, P);
956
+ deadlock = ctx.And(deadlock, ctx.Not(enabled));
957
+ }
958
+ return deadlock;
959
+ }
960
+ function encodeInvariantConstraints(ctx, invariants, mVars, P) {
961
+ let result = ctx.Bool.val(true);
962
+ for (const inv of invariants) {
963
+ let sum = ctx.Int.val(0);
964
+ for (const idx of inv.support) {
965
+ if (idx < P) {
966
+ sum = sum.add(mVars[idx].mul(inv.weights[idx]));
967
+ }
968
+ }
969
+ result = ctx.And(result, sum.eq(inv.constant));
970
+ }
971
+ return result;
972
+ }
973
+
974
+ // src/verification/z3/counterexample-decoder.ts
975
+ function decode(ctx, answer, flatNet) {
976
+ const trace = [];
977
+ const transitions = [];
978
+ if (answer == null) {
979
+ return { trace, transitions };
980
+ }
981
+ try {
982
+ extractTrace(ctx, answer, flatNet, trace, transitions);
983
+ } catch {
984
+ }
985
+ return { trace, transitions };
986
+ }
987
+ function extractTrace(ctx, expr, flatNet, trace, transitions) {
988
+ if (expr == null) return;
989
+ if (!ctx.isApp(expr)) return;
990
+ let name;
991
+ try {
992
+ const decl = expr.decl();
993
+ name = String(decl.name());
994
+ } catch {
995
+ return;
996
+ }
997
+ const P = flatNet.places.length;
998
+ if (name === "Reachable") {
999
+ const numArgs = expr.numArgs();
1000
+ if (numArgs === P) {
1001
+ const marking = extractMarking(ctx, expr, flatNet);
1002
+ if (marking != null) {
1003
+ trace.push(marking);
1004
+ }
1005
+ }
1006
+ }
1007
+ try {
1008
+ const numArgs = expr.numArgs();
1009
+ for (let i = 0; i < numArgs; i++) {
1010
+ const child = expr.arg(i);
1011
+ extractTrace(ctx, child, flatNet, trace, transitions);
1012
+ }
1013
+ } catch {
1014
+ }
1015
+ if (name.startsWith("t_")) {
1016
+ transitions.push(name.substring(2));
1017
+ }
1018
+ }
1019
+ function extractMarking(ctx, reachableApp, flatNet) {
1020
+ const P = flatNet.places.length;
1021
+ if (reachableApp.numArgs() !== P) return null;
1022
+ const builder = MarkingState.builder();
1023
+ for (let i = 0; i < P; i++) {
1024
+ const arg = reachableApp.arg(i);
1025
+ if (ctx.isIntVal(arg)) {
1026
+ const tokens = Number(arg.value());
1027
+ if (tokens > 0) {
1028
+ builder.tokens(flatNet.places[i], tokens);
1029
+ }
1030
+ } else {
1031
+ return null;
1032
+ }
1033
+ }
1034
+ return builder.build();
1035
+ }
1036
+
1037
+ // src/verification/smt-verifier.ts
1038
+ var SmtVerifier = class _SmtVerifier {
1039
+ constructor(net) {
1040
+ this.net = net;
1041
+ }
1042
+ _initialMarking = MarkingState.empty();
1043
+ _property = deadlockFree();
1044
+ _environmentPlaces = /* @__PURE__ */ new Set();
1045
+ _sinkPlaces = /* @__PURE__ */ new Set();
1046
+ _environmentMode = unbounded();
1047
+ _timeoutMs = 6e4;
1048
+ static forNet(net) {
1049
+ return new _SmtVerifier(net);
1050
+ }
1051
+ initialMarking(arg) {
1052
+ if (arg instanceof MarkingState) {
1053
+ this._initialMarking = arg;
1054
+ } else {
1055
+ const builder = MarkingState.builder();
1056
+ arg(builder);
1057
+ this._initialMarking = builder.build();
1058
+ }
1059
+ return this;
1060
+ }
1061
+ property(property) {
1062
+ this._property = property;
1063
+ return this;
1064
+ }
1065
+ environmentPlaces(...places) {
1066
+ for (const p of places) this._environmentPlaces.add(p);
1067
+ return this;
1068
+ }
1069
+ environmentMode(mode) {
1070
+ this._environmentMode = mode;
1071
+ return this;
1072
+ }
1073
+ /**
1074
+ * Declares expected sink (terminal) places for deadlock-freedom analysis.
1075
+ * Markings where any sink place has a token are not considered deadlocks.
1076
+ */
1077
+ sinkPlaces(...places) {
1078
+ for (const p of places) this._sinkPlaces.add(p);
1079
+ return this;
1080
+ }
1081
+ timeout(ms) {
1082
+ this._timeoutMs = ms;
1083
+ return this;
1084
+ }
1085
+ /**
1086
+ * Runs the verification pipeline.
1087
+ */
1088
+ async verify() {
1089
+ const start = performance.now();
1090
+ const report = [];
1091
+ report.push("=== IC3/PDR SAFETY VERIFICATION ===\n");
1092
+ report.push(`Net: ${this.net.name}`);
1093
+ const propDesc = this._sinkPlaces.size === 0 ? propertyDescription(this._property) : `${propertyDescription(this._property)} (sinks: ${[...this._sinkPlaces].map((p) => p.name).join(", ")})`;
1094
+ report.push(`Property: ${propDesc}`);
1095
+ report.push(`Timeout: ${(this._timeoutMs / 1e3).toFixed(0)}s
1096
+ `);
1097
+ report.push("Phase 1: Flattening net...");
1098
+ const flatNet = flatten(this.net, this._environmentPlaces, this._environmentMode);
1099
+ report.push(` Places: ${flatNet.places.length}`);
1100
+ report.push(` Transitions (expanded): ${flatNet.transitions.length}`);
1101
+ if (flatNet.environmentBounds.size > 0) {
1102
+ report.push(` Environment bounds: ${flatNet.environmentBounds.size} places`);
1103
+ }
1104
+ report.push("");
1105
+ report.push("Phase 2: Structural pre-check (siphon/trap)...");
1106
+ const structResult = structuralCheck(flatNet, this._initialMarking);
1107
+ let structResultStr;
1108
+ switch (structResult.type) {
1109
+ case "no-potential-deadlock":
1110
+ structResultStr = "no potential deadlock";
1111
+ break;
1112
+ case "potential-deadlock":
1113
+ structResultStr = `potential deadlock (siphon: {${[...structResult.siphon].join(",")}})`;
1114
+ break;
1115
+ case "inconclusive":
1116
+ structResultStr = `inconclusive (${structResult.reason})`;
1117
+ break;
1118
+ }
1119
+ report.push(` Result: ${structResultStr}
1120
+ `);
1121
+ if (this._property.type === "deadlock-free" && this._sinkPlaces.size === 0 && structResult.type === "no-potential-deadlock") {
1122
+ report.push("=== RESULT ===\n");
1123
+ report.push("PROVEN (structural): Deadlock-freedom verified by Commoner's theorem.");
1124
+ report.push(" All siphons contain initially marked traps.");
1125
+ return buildResult(
1126
+ { type: "proven", method: "structural", inductiveInvariant: null },
1127
+ report.join("\n"),
1128
+ [],
1129
+ [],
1130
+ [],
1131
+ [],
1132
+ performance.now() - start,
1133
+ { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: 0, structuralResult: structResultStr }
1134
+ );
1135
+ }
1136
+ report.push("Phase 3: Computing P-invariants...");
1137
+ const matrix = IncidenceMatrix.from(flatNet);
1138
+ const invariants = computePInvariants(matrix, flatNet, this._initialMarking);
1139
+ report.push(` Found: ${invariants.length} P-invariant(s)`);
1140
+ const structurallyBounded = isCoveredByInvariants(invariants, flatNet.places.length);
1141
+ report.push(` Structurally bounded: ${structurallyBounded ? "YES" : "NO"}`);
1142
+ for (const inv of invariants) {
1143
+ report.push(` ${formatInvariant(inv, flatNet)}`);
1144
+ }
1145
+ report.push("");
1146
+ report.push("Phase 4: IC3/PDR verification via Z3 Spacer...");
1147
+ let runner;
1148
+ try {
1149
+ runner = await createSpacerRunner(this._timeoutMs);
1150
+ } catch (e) {
1151
+ report.push(` ERROR: ${e.message ?? e}
1152
+ `);
1153
+ report.push("=== RESULT ===\n");
1154
+ report.push(`UNKNOWN: Z3 initialization error: ${e.message ?? e}`);
1155
+ return buildResult(
1156
+ { type: "unknown", reason: `Z3 init error: ${e.message ?? e}` },
1157
+ report.join("\n"),
1158
+ invariants,
1159
+ [],
1160
+ [],
1161
+ [],
1162
+ performance.now() - start,
1163
+ { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
1164
+ );
1165
+ }
1166
+ try {
1167
+ const encoding = encode(runner.ctx, runner.fp, flatNet, this._initialMarking, this._property, invariants, this._sinkPlaces);
1168
+ const queryResult = await runner.query(encoding.errorExpr, encoding.reachableDecl);
1169
+ switch (queryResult.type) {
1170
+ case "proven": {
1171
+ report.push(" Status: UNSAT (property holds)\n");
1172
+ const discoveredInvariants = [];
1173
+ if (queryResult.invariantFormula != null) {
1174
+ discoveredInvariants.push(substituteNames(queryResult.invariantFormula, flatNet));
1175
+ }
1176
+ for (const level of queryResult.levelInvariants) {
1177
+ discoveredInvariants.push(substituteNames(level, flatNet));
1178
+ }
1179
+ if (discoveredInvariants.length > 0) {
1180
+ report.push("Phase 5: Inductive invariant (discovered by IC3)");
1181
+ report.push(` Spacer synthesized: ${discoveredInvariants[0]}`);
1182
+ report.push(" This formula is INDUCTIVE: preserved by all transitions.");
1183
+ if (discoveredInvariants.length > 1) {
1184
+ report.push(" Per-level clauses:");
1185
+ for (let i = 1; i < discoveredInvariants.length; i++) {
1186
+ report.push(` ${discoveredInvariants[i]}`);
1187
+ }
1188
+ }
1189
+ report.push("");
1190
+ }
1191
+ report.push("=== RESULT ===\n");
1192
+ report.push(`PROVEN (IC3/PDR): ${propDesc}`);
1193
+ report.push(" Z3 Spacer proved no reachable state violates the property.");
1194
+ report.push(" NOTE: Verification ignores timing constraints and JS guards.");
1195
+ report.push(" An untimed proof is STRONGER than a timed one (timing only restricts behavior).");
1196
+ return buildResult(
1197
+ {
1198
+ type: "proven",
1199
+ method: "IC3/PDR",
1200
+ inductiveInvariant: queryResult.invariantFormula != null ? substituteNames(queryResult.invariantFormula, flatNet) : null
1201
+ },
1202
+ report.join("\n"),
1203
+ invariants,
1204
+ discoveredInvariants,
1205
+ [],
1206
+ [],
1207
+ performance.now() - start,
1208
+ { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
1209
+ );
1210
+ }
1211
+ case "violated": {
1212
+ report.push(" Status: SAT (counterexample found)\n");
1213
+ const decoded = decode(runner.ctx, queryResult.answer, flatNet);
1214
+ report.push("=== RESULT ===\n");
1215
+ report.push(`VIOLATED: ${propDesc}`);
1216
+ if (decoded.trace.length > 0) {
1217
+ report.push(` Counterexample trace (${decoded.trace.length} states):`);
1218
+ for (let i = 0; i < decoded.trace.length; i++) {
1219
+ report.push(` ${i}: ${decoded.trace[i]}`);
1220
+ }
1221
+ }
1222
+ if (decoded.transitions.length > 0) {
1223
+ report.push(` Firing sequence: ${decoded.transitions.join(" -> ")}`);
1224
+ }
1225
+ report.push("\n WARNING: This counterexample is in UNTIMED semantics.");
1226
+ report.push(" It may be spurious if timing constraints prevent this sequence.");
1227
+ report.push(" JS guards are also ignored in this analysis.");
1228
+ return buildResult(
1229
+ { type: "violated" },
1230
+ report.join("\n"),
1231
+ invariants,
1232
+ [],
1233
+ decoded.trace,
1234
+ decoded.transitions,
1235
+ performance.now() - start,
1236
+ { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
1237
+ );
1238
+ }
1239
+ case "unknown": {
1240
+ report.push(` Status: UNKNOWN (${queryResult.reason})
1241
+ `);
1242
+ report.push("=== RESULT ===\n");
1243
+ report.push(`UNKNOWN: Could not determine ${propDesc}`);
1244
+ report.push(` Reason: ${queryResult.reason}`);
1245
+ return buildResult(
1246
+ { type: "unknown", reason: queryResult.reason },
1247
+ report.join("\n"),
1248
+ invariants,
1249
+ [],
1250
+ [],
1251
+ [],
1252
+ performance.now() - start,
1253
+ { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
1254
+ );
1255
+ }
1256
+ }
1257
+ } catch (e) {
1258
+ report.push(` ERROR: ${e.message ?? e}
1259
+ `);
1260
+ report.push("=== RESULT ===\n");
1261
+ report.push(`UNKNOWN: Z3 solver error: ${e.message ?? e}`);
1262
+ return buildResult(
1263
+ { type: "unknown", reason: `Z3 error: ${e.message ?? e}` },
1264
+ report.join("\n"),
1265
+ invariants,
1266
+ [],
1267
+ [],
1268
+ [],
1269
+ performance.now() - start,
1270
+ { places: flatNet.places.length, transitions: flatNet.transitions.length, invariantsFound: invariants.length, structuralResult: structResultStr }
1271
+ );
1272
+ } finally {
1273
+ runner.dispose();
1274
+ }
1275
+ }
1276
+ };
1277
+ function substituteNames(formula, flatNet) {
1278
+ for (let i = flatNet.places.length - 1; i >= 0; i--) {
1279
+ formula = formula.replace(new RegExp(`\\bm${i}\\b`, "g"), flatNet.places[i].name);
1280
+ }
1281
+ return formula;
1282
+ }
1283
+ function formatInvariant(inv, flatNet) {
1284
+ const parts = [];
1285
+ for (const idx of inv.support) {
1286
+ if (inv.weights[idx] !== 1) {
1287
+ parts.push(`${inv.weights[idx]}*${flatNet.places[idx].name}`);
1288
+ } else {
1289
+ parts.push(flatNet.places[idx].name);
1290
+ }
1291
+ }
1292
+ return `${parts.join(" + ")} = ${inv.constant}`;
1293
+ }
1294
+ function buildResult(verdict, report, invariants, discoveredInvariants, trace, transitions, elapsedMs, statistics) {
1295
+ return { verdict, report, invariants, discoveredInvariants, counterexampleTrace: trace, counterexampleTransitions: transitions, elapsedMs, statistics };
1296
+ }
1297
+
1298
+ // src/verification/smt-verification-result.ts
1299
+ function isProven(result) {
1300
+ return result.verdict.type === "proven";
1301
+ }
1302
+ function isViolated(result) {
1303
+ return result.verdict.type === "violated";
1304
+ }
1305
+
1306
+ export {
1307
+ and,
1308
+ andPlaces,
1309
+ xor,
1310
+ xorPlaces,
1311
+ outPlace,
1312
+ timeout,
1313
+ timeoutPlace,
1314
+ forwardInput,
1315
+ allPlaces,
1316
+ enumerateBranches,
1317
+ MarkingState,
1318
+ MarkingStateBuilder,
1319
+ deadlockFree,
1320
+ mutualExclusion,
1321
+ placeBound,
1322
+ unreachable,
1323
+ propertyDescription,
1324
+ flatTransition,
1325
+ unbounded,
1326
+ bounded,
1327
+ flatten,
1328
+ IncidenceMatrix,
1329
+ pInvariant,
1330
+ pInvariantToString,
1331
+ computePInvariants,
1332
+ isCoveredByInvariants,
1333
+ structuralCheck,
1334
+ findMinimalSiphons,
1335
+ findMaximalTrapIn,
1336
+ createSpacerRunner,
1337
+ flatNetPlaceCount,
1338
+ flatNetTransitionCount,
1339
+ flatNetIndexOf,
1340
+ encode,
1341
+ decode,
1342
+ SmtVerifier,
1343
+ isProven,
1344
+ isViolated
1345
+ };
1346
+ //# sourceMappingURL=chunk-H62Z76FY.js.map