libpetri 4.1.0 → 5.1.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,5 +1,5 @@
1
- import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aO as EnvironmentAnalysisMode, aP as PInvariant, aQ as MarkingState, aR as SmtProperty, aS as MarkingStateBuilder, aT as SmtVerificationResult } from '../petri-net-p0ONpSAO.js';
2
- export { aU as BranchPlaceBound, aV as DeadlockFree, aW as JoinedOrDeadLettered, aX as MutualExclusion, aY as PlaceBound, aZ as Proven, a_ as SmtStatistics, a$ as TokenSupplier, b0 as Unknown, b1 as Unreachable, b2 as Verdict, $ as VerificationHarness, a0 as VerificationResult, b3 as Violated, b4 as alwaysAvailable, b4 as analysisAlwaysAvailable, b5 as analysisBounded, b6 as analysisIgnore, b5 as bounded, b7 as branchPlaceBound, b8 as deadlockFree, b6 as ignore, b9 as isProven, ba as isViolated, bb as joinedOrDeadLettered, bc as mutualExclusion, bd as pInvariant, be as pInvariantToString, bf as placeBound, bg as propertyDescription, bh as unreachable } from '../petri-net-p0ONpSAO.js';
1
+ import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aO as EnvironmentAnalysisMode, aP as PInvariant, aQ as MarkingState, aR as SmtProperty, aS as Verdict, aT as MarkingStateBuilder, aU as SmtVerificationResult } from '../petri-net-6G5r1Wwb.js';
2
+ export { aV as BranchPlaceBound, aW as DeadlockFree, aX as JoinedOrDeadLettered, aY as MutualExclusion, aZ as PlaceBound, a_ as Proven, a$ as SmtStatistics, b0 as TerminatesAtSink, b1 as TokenSupplier, b2 as Unknown, b3 as Unreachable, $ as VerificationHarness, a0 as VerificationResult, b4 as VerificationRoute, b5 as Violated, b6 as alwaysAvailable, b6 as analysisAlwaysAvailable, b7 as analysisBounded, b8 as analysisIgnore, b7 as bounded, b9 as branchPlaceBound, ba as deadlockFree, b8 as ignore, bb as isProven, bc as isViolated, bd as joinedOrDeadLettered, be as mutualExclusion, bf as pInvariant, bg as pInvariantToString, bh as placeBound, bi as propertyDescription, bj as terminatesAtSink, bk as unreachable } from '../petri-net-6G5r1Wwb.js';
3
3
 
4
4
  /**
5
5
  * A flattened transition with pre/post vectors for SMT encoding.
@@ -253,6 +253,182 @@ declare function findMinimalSiphons(flatNet: FlatNet): ReadonlySet<number>[];
253
253
  */
254
254
  declare function findMaximalTrapIn(flatNet: FlatNet, places: ReadonlySet<number>): ReadonlySet<number>;
255
255
 
256
+ /**
257
+ * @module rest-set
258
+ *
259
+ * Where a token may come to rest without being stranded (VER-002, VER-014).
260
+ *
261
+ * `DeadlockFree` is violated by a quiescent marking that holds a token outside the
262
+ * places where resting is permitted. The permitted set has two layers:
263
+ *
264
+ * - the **declared sinks** (`SmtVerifier.sinkPlaces`), where a token may always rest;
265
+ * - the **conditional sinks** (`SmtVerifier.sinkPlacesWhen(marker, …)`), where a
266
+ * token may rest only while `marker` holds a token. A marked marker is a
267
+ * *designed terminal* — a halted or paused run — and the marker itself is at rest
268
+ * whenever it is marked.
269
+ *
270
+ * Declarations union: a token in `p` is excused when `p` is a declared sink, when
271
+ * `p` is a marker, or when some conditional set naming `p` has its marker marked.
272
+ * Every route that decides `DeadlockFree` — the flat and name-coloured CHC encoders,
273
+ * the abstract counterexample replay and the Route B name-partition graph — reads
274
+ * this one module, so the predicate cannot drift between them (VER-002 AC7).
275
+ *
276
+ * `TerminatesAtSink` is untouched by conditional declarations: it asks whether a
277
+ * declared sink was reached and reads only the unconditional set.
278
+ */
279
+
280
+ /** Places where a token may rest while `marker` holds a token. */
281
+ interface ConditionalSinks {
282
+ readonly marker: Place<any>;
283
+ readonly places: ReadonlySet<Place<any>>;
284
+ }
285
+ /**
286
+ * Per flat place, how a token resting there is excused: `null` when it never counts
287
+ * as stranded (a declared sink, or a marker), otherwise the ascending flat indices of
288
+ * the markers whose presence excuses it — empty when nothing does, so a token there
289
+ * is stranded whenever the marking is quiescent.
290
+ *
291
+ * Places and markers that do not resolve in the flat net contribute nothing, as an
292
+ * unresolved sink does: a mistyped marker makes the property stricter, never laxer.
293
+ */
294
+ declare function strandingExcuses(flatNet: FlatNet, sinkPlaces: ReadonlySet<Place<any>>, conditional: readonly ConditionalSinks[]): (readonly number[] | null)[];
295
+ /**
296
+ * Whether `m` holds a token that is stranded — outside every place where resting is
297
+ * permitted in `m`. The graph-route form of {@link strandingExcuses}.
298
+ */
299
+ declare function strandsToken(m: MarkingState, sinkPlaces: ReadonlySet<Place<any>>, conditional: readonly ConditionalSinks[]): boolean;
300
+ /**
301
+ * The declarations as the report prints them after the property description:
302
+ * `sinks: a, b; when h: c, d; when p`, or `null` when nothing is declared.
303
+ * Declaration order throughout, so the four implementations render the same text.
304
+ */
305
+ declare function describeSinks(sinkPlaces: ReadonlySet<Place<any>>, conditional: readonly ConditionalSinks[]): string | null;
306
+
307
+ /**
308
+ * @module programming-error
309
+ *
310
+ * Telling a verification failure apart from a bug.
311
+ *
312
+ * The pipeline is full of `catch` blocks that turn a failure into `Unknown`, or
313
+ * into a weaker but still well-formed result. Every one of them was written for a
314
+ * real condition — the solver died, the transport timed out, the replay search ran
315
+ * out of budget — and every one of them quietly acquires a second meaning: *any*
316
+ * defect in the code it guards. Once both arrive as the same verdict they cannot
317
+ * be told apart, and a bug becomes a permanently plausible weaker answer instead
318
+ * of a loud failure. That is the most expensive failure shape this verifier has:
319
+ * a report that looks right and proves less.
320
+ *
321
+ * So the taxonomy is explicit. A `TypeError` or `ReferenceError` is never a
322
+ * verdict — it is a defect in libpetri or in a caller's net, and it propagates. A
323
+ * `RangeError` *is* a verdict: a stack overflow on a deep net is the capacity
324
+ * limit `Unknown` exists to report. Everything else — a dead solver, a bad reply,
325
+ * an exhausted budget — is the condition the catch was written for and passes
326
+ * through untouched.
327
+ */
328
+ /**
329
+ * Re-throws `e` when it is a programming defect rather than a verification
330
+ * outcome. Call it first in any `catch` that degrades a result.
331
+ */
332
+ declare function rethrowIfProgrammingError(e: unknown): void;
333
+
334
+ /**
335
+ * @module graph-decision
336
+ *
337
+ * The property predicate both state-class-graph routes decide, in one place.
338
+ *
339
+ * Two routes enumerate a finite graph of classes and read a verdict off it: the
340
+ * ν name-partition quotient of [VER-012] (`nu-scg-verifier`) and the plain
341
+ * bounded enumeration of [VER-017] (`scg-verifier`). They explore different
342
+ * graphs, but the question they ask of a class is identical, and [VER-002] AC7
343
+ * requires every route to decide the *same* predicate. Stating it once is what
344
+ * keeps that true: when the sink clause last lived in two copies, one of them
345
+ * drifted (NU-040 AC4).
346
+ */
347
+
348
+ /** A finite graph of classes, indexed `0 .. count - 1`, class 0 the initial one. */
349
+ interface ClassView {
350
+ readonly count: number;
351
+ /** The marking of class `i`. */
352
+ markingOf(i: number): MarkingState;
353
+ /** Whether class `i` has no successor — the graph's quiescence. */
354
+ isQuiescent(i: number): boolean;
355
+ }
356
+ /**
357
+ * The index of the first class witnessing a violation, or `-1` when the property
358
+ * holds across the whole graph.
359
+ *
360
+ * Quiescence-based properties read `isQuiescent`; reachability-safety properties
361
+ * read the marking alone. `DeadlockFree` uses the shared rest set of [VER-014],
362
+ * so a conditional sink excuses a token exactly as it does in the encoders.
363
+ */
364
+ declare function decideOverClasses(view: ClassView, property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, conditionalSinks?: readonly ConditionalSinks[]): number;
365
+
366
+ /**
367
+ * @module scg-verifier
368
+ *
369
+ * Bounded state-space enumeration ([VER-017]): decide a property by building the
370
+ * state-class graph and reading the verdict off it, when the graph closes within
371
+ * a class budget.
372
+ *
373
+ * IC3/PDR is built for state spaces that are wide and shallow. A workflow net is
374
+ * the opposite — narrow and deep: a forty-node pipeline has under two thousand
375
+ * reachable classes, but its diameter is the length of the pipeline, so the
376
+ * fixpoint engine needs a frame per stage and its cost climbs with the cube of
377
+ * the length. Enumerating the same net is linear in the state space and finishes
378
+ * in milliseconds. Measured on a forty-node chain (370 places): 410 s on the
379
+ * fixpoint path, 0.11 s here.
380
+ *
381
+ * The route is exact when the graph closes — sound *and* complete, so a
382
+ * `violated` is a real firing sequence rather than a possibly-spurious
383
+ * over-approximation, and a `proven` is never the `unknown` a fixpoint search
384
+ * runs out of time for.
385
+ *
386
+ * It applies only to an **untimed** net — every transition `immediate` — and that
387
+ * restriction is what makes the verdict interchangeable with the encoders'. The
388
+ * state-class graph carries firing domains, so on a timed net it would explore
389
+ * only the runs the timing admits and its `proven` would be the weaker timed
390
+ * claim; [VER-004] is explicit that the untimed proof is the stronger one, and a
391
+ * route must not quietly hand back a weaker claim than the one it replaced. On an
392
+ * untimed net no domain excludes anything, the graph explores exactly the untimed
393
+ * reachable set, and the two routes decide the same predicate over the same
394
+ * abstraction — enumeration simply decides it where the search may not.
395
+ *
396
+ * When the graph does not close within the budget the route declines and the
397
+ * caller runs the SMT pipeline unchanged: enumeration never turns a verdict into
398
+ * `unknown` that the solver could have decided.
399
+ */
400
+
401
+ /**
402
+ * Whether every transition is `immediate`, so the state-class graph explores the
403
+ * untimed reachable set exactly and its verdict is the encoders' claim rather
404
+ * than the weaker timed one. See this module's header.
405
+ */
406
+ declare function isUntimed(net: PetriNet): boolean;
407
+ /** The note a decided verdict carries into the report. */
408
+ declare const NOTE_ENUMERATED: string;
409
+ /** Outcome of the enumeration route. */
410
+ type ScgOutcome =
411
+ /** The graph closed and decided the property. */
412
+ {
413
+ readonly kind: 'decided';
414
+ readonly verdict: Verdict;
415
+ readonly trace: MarkingState[];
416
+ readonly transitions: string[];
417
+ readonly classCount: number;
418
+ }
419
+ /** The graph hit the class budget; the caller falls through to the SMT pipeline. */
420
+ | {
421
+ readonly kind: 'truncated';
422
+ readonly classCount: number;
423
+ };
424
+ /**
425
+ * Decides `property` by enumeration, or reports truncation.
426
+ *
427
+ * @param maxClasses the class budget; `<= 0` disables the route (the caller then
428
+ * never calls this).
429
+ */
430
+ declare function verifyViaStateClassGraph(net: PetriNet, initial: MarkingState, property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, maxClasses: number, conditionalSinks?: readonly ConditionalSinks[]): ScgOutcome;
431
+
256
432
  /** Environment variable naming the z3 executable (default: `z3` on `PATH`). */
257
433
  declare const Z3_ENV = "LIBPETRI_Z3";
258
434
  /** Environment variable naming a directory that receives every script and reply. */
@@ -406,14 +582,15 @@ type CertificateCheckOutcome = {
406
582
  * @param sinkPlaces declared sink places (deadlock-freedom VC3)
407
583
  * @param solver the resolved z3 executable
408
584
  * @param timeoutMs per-invocation solver budget in milliseconds
585
+ * @param conditionalSinks conditional sink declarations (VER-014, deadlock-freedom VC3)
409
586
  */
410
- declare function checkCertificate(certificate: string | null, flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, invariants: readonly PInvariant[], sinkPlaces: ReadonlySet<Place<any>>, solver: Z3Solver, timeoutMs: number): Promise<CertificateCheckOutcome>;
587
+ declare function checkCertificate(certificate: string | null, flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, invariants: readonly PInvariant[], sinkPlaces: ReadonlySet<Place<any>>, solver: Z3Solver, timeoutMs: number, conditionalSinks?: readonly ConditionalSinks[], stateEquation?: boolean): Promise<CertificateCheckOutcome>;
411
588
  /**
412
589
  * The certificate-check script for the given inputs, exactly as
413
590
  * {@link checkCertificate} would send it (VER-013 script parity): what the
414
591
  * cross-language golden tests diff.
415
592
  */
416
- declare function vcScript(certificate: string, flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, invariants: readonly PInvariant[]): string;
593
+ declare function vcScript(certificate: string, flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, invariants: readonly PInvariant[], conditionalSinks?: readonly ConditionalSinks[], stateEquation?: boolean): string;
417
594
 
418
595
  /**
419
596
  * Name-correlation fragment classifier for the ν-aware state class graph
@@ -492,13 +669,17 @@ declare class SmtVerifier {
492
669
  private _property;
493
670
  private readonly _environmentPlaces;
494
671
  private readonly _sinkPlaces;
672
+ private readonly _conditionalSinks;
495
673
  private readonly _budgetPlaces;
496
674
  private _environmentMode;
497
675
  private _timeoutMs;
498
676
  private _certificateCheck;
499
677
  private _counterexampleReplay;
500
678
  private _semiflowInvariants;
679
+ private _stateEquation;
680
+ private _linearBound;
501
681
  private _nuMaxClasses;
682
+ private _enumerationMaxClasses;
502
683
  private _fragmentMode;
503
684
  private readonly _carrierPlaces;
504
685
  private _prioritySemantics;
@@ -510,10 +691,36 @@ declare class SmtVerifier {
510
691
  environmentPlaces(...places: EnvironmentPlace<any>[]): this;
511
692
  environmentMode(mode: EnvironmentAnalysisMode): this;
512
693
  /**
513
- * Declares expected sink (terminal) places for deadlock-freedom analysis.
514
- * Markings where any sink place has a token are not considered deadlocks.
694
+ * Declares expected sink (terminal) places for deadlock-freedom analysis
695
+ * (VER-002): a token resting in one is never stranded, and `TerminatesAtSink`
696
+ * asks whether one of them was reached.
515
697
  */
516
698
  sinkPlaces(...places: Place<any>[]): this;
699
+ /**
700
+ * Declares places where a token may rest **while `marker` holds a token**
701
+ * (VER-014) — a designed terminal such as a halt or pause marker, under which
702
+ * the work it interrupted legitimately stays where it was delivered.
703
+ *
704
+ * `DeadlockFree` then reads a quiescent marking against the union of the
705
+ * declared sinks, the markers, and every conditional set whose marker is marked:
706
+ * a token in `p` is stranded only when none of those excuse it. The marker
707
+ * itself is at rest whenever it is marked, so `sinkPlacesWhen(halt)` with no
708
+ * further places excuses exactly the halt token. Repeated calls for one marker
709
+ * accumulate; declarations for several markers union. `TerminatesAtSink` is
710
+ * unaffected and reads only {@link sinkPlaces}.
711
+ *
712
+ * ```ts
713
+ * SmtVerifier.forNet(net)
714
+ * .property(deadlockFree())
715
+ * .sinkPlaces(done) // may always rest
716
+ * .sinkPlacesWhen(halt, inbox, pending) // may rest once the run halted
717
+ * .sinkPlacesWhen(pause, inbox) // may rest while paused
718
+ * ```
719
+ *
720
+ * An unresolved marker or place contributes nothing, as an unresolved sink
721
+ * does: a mistyped marker makes the property stricter, never laxer.
722
+ */
723
+ sinkPlacesWhen(marker: Place<any>, ...places: Place<any>[]): this;
517
724
  /**
518
725
  * Declares ν-net budget places (NU-040): places whose token count bounds the
519
726
  * live correlation pool (they gate fresh-name minting). Declaring at least one
@@ -579,7 +786,58 @@ declare class SmtVerifier {
579
786
  * `Certificate check: not applicable (name-coloured encoding)`. Off by default so
580
787
  * reports stay byte-equal.
581
788
  */
582
- semiflowInvariants(enabled: boolean): this;
789
+ /**
790
+ * `'auto'` decides whether the semiflows would add **information to the
791
+ * encoding**, which is not the same question as whether they would appear in
792
+ * {@link SmtVerificationResult.invariants} for a caller who reads them.
793
+ *
794
+ * A complete basis spans every conservation law of the net, so a semiflow it
795
+ * spans constrains nothing further and IC3 gains nothing from it — that is why
796
+ * `'auto'` skips the enumeration there. But the basis is the *signed*
797
+ * null-space, and a law it spans need not appear in it in **non-negative**
798
+ * form; only the Farkas enumeration produces that. A caller inspecting the
799
+ * invariant list for a law of a given shape — "a non-negative law weighting the
800
+ * budget place and every running place positively" — can therefore find nothing
801
+ * on a net that plainly has one. Such a caller should ask for the union
802
+ * explicitly: `'auto'` is the setting to prefer for verification, not for
803
+ * harvesting.
804
+ */
805
+ semiflowInvariants(enabled: boolean | 'auto'): this;
806
+ /**
807
+ * Enables/disables the linear state-equation bound phase (VER-015; default:
808
+ * enabled). A reachability-safety property whose violating markings exceed some
809
+ * `y·M <= y·M0` with `y >= 0`, `y·C <= 0` is then proven structurally, from one
810
+ * linear query re-checked in exact integer arithmetic, before any fixpoint search.
811
+ * Disable it to force the IC3/PDR path — for its certificate, or to exercise the
812
+ * fixpoint engine itself.
813
+ */
814
+ linearBound(enabled: boolean): this;
815
+ /**
816
+ * Encodes the **state equation** with firing counters (VER-016; default:
817
+ * disabled — the encoding then carries places only).
818
+ *
819
+ * The flat encoding gains one counter `n_t` per flat transition and every
820
+ * transition rule conjoins the marking equation `M' = M0 + C·n'` for each place
821
+ * whose column is exact (no consume-all / reset arc, not injected). Every linear
822
+ * consequence of the marking equation — the equality laws of VER-005/VER-007
823
+ * **and** the inequality laws `y·M ≤ y·M0` (`y ≥ 0, y·C ≤ 0`) and their mixed-sign
824
+ * kin, which are what an *ordering* argument ("both join slots armed means every
825
+ * upstream stage has run, so nothing can still halt") looks like in linear
826
+ * arithmetic — is then available to Spacer as a fact rather than a lemma it has to
827
+ * invent. On a 50-place agent-dispatch workflow, proper completion under conditional
828
+ * sinks went from `unknown` after 120 s to `proven` in 1.5 s with this as the only
829
+ * change; a 53-place pipeline stage before a join, `unknown` at 300 s, proves in
830
+ * under a second.
831
+ *
832
+ * The cost is a larger state (places + transitions) and a slower witness search
833
+ * on genuinely violated properties (about 1.5× on the nets above), so it is opt-in.
834
+ * Soundness is unchanged: the counters are exact bookkeeping, the equation holds
835
+ * on every reachable state by construction (`Strengthening.lean`, the same shape
836
+ * as the equality laws), and the certificate check re-proves it against the raw
837
+ * step relation, whose only counter knowledge is the increment. Not applied to the
838
+ * name-coloured encoding or Route B, which the report says when it applies.
839
+ */
840
+ stateEquation(enabled: boolean): this;
583
841
  /**
584
842
  * Sets the class-count cap for the ν-aware state-class-graph analysis (NU-050,
585
843
  * Route B). When the symbolic name-aware graph would exceed this, the analysis
@@ -587,6 +845,26 @@ declare class SmtVerifier {
587
845
  * structurally bounded). Default 100_000.
588
846
  */
589
847
  nuMaxClasses(max: number): this;
848
+ /**
849
+ * Sets the class budget for the bounded state-space enumeration route
850
+ * (VER-017; default 50 000). `0` disables the route, so every query goes to
851
+ * the SMT pipeline.
852
+ *
853
+ * When the state-class graph closes within the budget the property is decided
854
+ * exactly — sound and complete over the timed semantics — and no solver runs.
855
+ * This is what makes a long pipeline tractable: IC3 needs a frame per stage and
856
+ * its cost climbs with the cube of the length, while enumeration is linear in
857
+ * the reachable state space. A forty-node chain (370 places, 1 967 classes)
858
+ * takes 410 s on the fixpoint path and 0.11 s here.
859
+ *
860
+ * The route declines when the graph exceeds the budget, and the SMT pipeline
861
+ * then runs unchanged — it can only add verdicts, never remove them. It is
862
+ * skipped for ν-nets, which have their own exact route (NU-050, Route B), for
863
+ * nets with environment places, whose injection the graph does not model, and
864
+ * for **timed** nets, where its verdict would be the weaker timed claim rather
865
+ * than the untimed one the encoders make (VER-004).
866
+ */
867
+ enumerationMaxClasses(max: number): this;
590
868
  /**
591
869
  * Selects the ν-net coloured-place fragment for Route B (NU-051). `base`
592
870
  * (default) admits the shipped mint → matched-join fragment only; `extended`
@@ -650,6 +928,14 @@ declare class SmtVerifier {
650
928
  * @throws Error if the net violates CORE-043 — verification rejects the same nets execution rejects.
651
929
  */
652
930
  verify(): Promise<SmtVerificationResult>;
931
+ /**
932
+ * Runs the linear state-equation bound query (VER-015) and re-checks its answer in
933
+ * exact integer arithmetic. Returns the bound as the report prints it when one
934
+ * separates the violation, `null` otherwise (no bound, solver inconclusive, or a
935
+ * model that failed the re-check — each named in the report). Never the last word:
936
+ * `null` hands over to the fixpoint query.
937
+ */
938
+ private linearBoundProof;
653
939
  /**
654
940
  * ν-net soundness guard (NU-040, NU-050). Applied only when the net contains
655
941
  * match (ν-join) transitions, and only to a proven/violated verdict (an
@@ -679,6 +965,11 @@ interface EncodedScripts {
679
965
  readonly certificate: string | null;
680
966
  /** Whether `horn` is the name-coloured encoding. */
681
967
  readonly coloured: boolean;
968
+ /**
969
+ * The linear state-equation bound query (VER-015), or `null` for a property with
970
+ * no linear demand (the quiescence properties).
971
+ */
972
+ readonly bound: string | null;
682
973
  }
683
974
  /**
684
975
  * `(define-fun Reachable ((x!0 Int) …) Bool true)`: the certificate stand-in the
@@ -744,6 +1035,11 @@ declare function runZ3Spacer(solver: Z3Solver, timeoutMs: number, smt2: string,
744
1035
  * 3. **Error**: `Error :- Reachable(M) ∧ violation(M)`; `(assert (not Error))`, so
745
1036
  * `sat` is PROVEN and `unsat` is VIOLATED
746
1037
  *
1038
+ * With the state equation (VER-016, {@link encodeNet}) the state is `(M, n)` — one
1039
+ * firing counter per flat transition — and every transition rule also conjoins
1040
+ * `M' = M0 + C·n'`, which hands Spacer every linear consequence of the marking
1041
+ * equation (the inequality conservation laws it cannot invent) at no enumeration cost.
1042
+ *
747
1043
  * The emitted script is byte-identical to the Rust reference (`smt_encoder.rs`) and
748
1044
  * the Java port for the same input: places in code-point order of their names, the
749
1045
  * property's places, sinks, env bounds and injections in place-index order,
@@ -754,16 +1050,48 @@ declare function runZ3Spacer(solver: Z3Solver, timeoutMs: number, smt2: string,
754
1050
  interface SmtEncoding {
755
1051
  /** The script text. */
756
1052
  readonly smt2: string;
757
- /** The number of flat places (the arity of `Reachable` in the flat encoding). */
1053
+ /** The number of flat places (the leading arguments of `Reachable` in the flat encoding). */
758
1054
  readonly placeCount: number;
1055
+ /**
1056
+ * The number of firing counters that follow the places in `Reachable` (VER-016):
1057
+ * one per flat transition when the state equation is encoded, else 0.
1058
+ */
1059
+ readonly counterCount: number;
1060
+ }
1061
+ /** Options of {@link encodeNet}. */
1062
+ interface EncodeOptions {
1063
+ /** Declared sink places (VER-002). */
1064
+ readonly sinkPlaces?: ReadonlySet<Place<any>>;
1065
+ /** Emit `:produce-proofs` and `(get-proof)` so an `unsat` reply carries the refutation the replay decodes. */
1066
+ readonly produceProofs?: boolean;
1067
+ /** Conditional sinks (VER-014); read by `deadlock-free` only. */
1068
+ readonly conditionalSinks?: readonly ConditionalSinks[];
1069
+ /**
1070
+ * Carry one firing counter per flat transition and conjoin the marking equation
1071
+ * `M' = M0 + C·n'` (VER-016) into every rule body. Off by default (scripts stay
1072
+ * byte-identical).
1073
+ */
1074
+ readonly stateEquation?: boolean;
759
1075
  }
760
1076
  /**
761
1077
  * Encodes the net and property as a HORN script.
762
1078
  *
763
1079
  * @param produceProofs emit `:produce-proofs` and `(get-proof)` so an `unsat` reply
764
1080
  * carries the refutation the replay decodes
1081
+ * @param conditionalSinks places where a token may rest while a marker is marked
1082
+ * (VER-014); read by `deadlock-free` only
1083
+ */
1084
+ declare function encode(flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, invariants: readonly PInvariant[], sinkPlaces?: ReadonlySet<Place<any>>, produceProofs?: boolean, conditionalSinks?: readonly ConditionalSinks[]): SmtEncoding;
1085
+ /**
1086
+ * {@link encode} with named options. With `stateEquation` (VER-016) the state carries
1087
+ * one firing counter per flat transition after the places: `Reachable(M, n)`, the
1088
+ * initial fact has `n = 0`, transition `k`'s rule increments `n_k` and copies the
1089
+ * others, an injection rule copies them all, and every transition rule's body
1090
+ * conjoins `m'_p = M0_p + Σ_t C[p][t]·n'_t` for each place whose column is exact
1091
+ * (no consume-all / reset arc, not injected) together with `n' ≥ 0`. The error rule
1092
+ * quantifies the counters and constrains only the marking.
765
1093
  */
766
- declare function encode(flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, invariants: readonly PInvariant[], sinkPlaces?: ReadonlySet<Place<any>>, produceProofs?: boolean): SmtEncoding;
1094
+ declare function encodeNet(flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, invariants: readonly PInvariant[], options?: EncodeOptions): SmtEncoding;
767
1095
  /**
768
1096
  * The net's one-step relation `T(M, M')` as one plain SMT-LIB2 formula over the free
769
1097
  * variables `m0..` / `m0p..`: the disjunction of every flat transition firing and
@@ -772,7 +1100,75 @@ declare function encode(flatNet: FlatNet, initialMarking: MarkingState, property
772
1100
  * path but omits the P-invariant conjuncts, so a certificate poisoned by a wrong
773
1101
  * invariant cannot re-certify itself.
774
1102
  */
775
- declare function encodeStepRelationSmt2(flatNet: FlatNet): string;
1103
+ declare function encodeStepRelationSmt2(flatNet: FlatNet, stateEquation?: boolean): string;
1104
+
1105
+ /**
1106
+ * @module linear-bound
1107
+ *
1108
+ * The linear state-equation bound (VER-015): a structural proof of a
1109
+ * reachability-safety property that needs no fixpoint search.
1110
+ *
1111
+ * Every reachable marking of the abstract net satisfies `M = M0 + C·σ` for some
1112
+ * firing count vector `σ ≥ 0`, so for any weighting `y ≥ 0` with `y·C ≤ 0` on every
1113
+ * transition, `y·M ≤ y·M0` holds along every run — a **decreasing** conservation
1114
+ * law, where the P-invariants of [VER-005] are the *equalities* `y·C = 0`. The
1115
+ * violation of a reachability-safety property is a lower demand on some places
1116
+ * (`m_p ≥ 1` for each place of an `unreachable`, `m_p ≥ k+1` for a `placeBound`);
1117
+ * if some `y` makes that demand exceed `y·M0`, no reachable marking meets it and the
1118
+ * property is proven.
1119
+ *
1120
+ * Finding `y` is one linear query in `QF_LIA`, answered by the same `z3` transport
1121
+ * as everything else ([VER-013]); the answer is then re-checked in exact integer
1122
+ * arithmetic (`y ≥ 0`, `y·C ≤ 0` per transition, `y·d ≥ y·M0 + 1`), so the proof
1123
+ * rests on the check, not on the solver. It closes exactly the class of proofs IC3
1124
+ * misses on pipeline-shaped nets: an ordering argument ("both join slots armed means
1125
+ * every upstream stage has run, so no halt is still possible") is a weighted count
1126
+ * bound, which Spacer's lemma generalisation does not invent over fifty variables
1127
+ * but a linear solver finds in milliseconds.
1128
+ *
1129
+ * Soundness needs the same guards as the equality laws: zero weight on every
1130
+ * consume-all / reset place (H1 — the fire relation is not linear there) and on
1131
+ * every injected environment place (H3' — injection breaks conservation).
1132
+ */
1133
+
1134
+ /** One linear bound `Σ weights[p]·m_p ≤ constant`, with the demand it separates. */
1135
+ interface LinearBound {
1136
+ /** `y`, one entry per flat place, all non-negative. */
1137
+ readonly weights: readonly bigint[];
1138
+ /** `y·M0`. */
1139
+ readonly constant: bigint;
1140
+ /** `y·d`, what the violating markings need at least; strictly above `constant`. */
1141
+ readonly demandValue: bigint;
1142
+ }
1143
+ /**
1144
+ * The violation's demand: flat place index → the least count a violating marking
1145
+ * holds there. `null` when the property is not a reachability-safety property, or
1146
+ * names no place the net resolves (the verifier refuses those before this runs).
1147
+ */
1148
+ declare function violationDemand(flatNet: FlatNet, property: SmtProperty): Map<number, number> | null;
1149
+ /**
1150
+ * The `QF_LIA` script asking for a separating `y`, or `null` when the property has
1151
+ * no linear demand. Byte-identical across the four implementations: places in flat
1152
+ * index order, one row per flat transition in net order, `(- k)` for a negative
1153
+ * literal, a lone term unwrapped.
1154
+ */
1155
+ declare function encodeLinearBound(flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty): string | null;
1156
+ /**
1157
+ * The weighting in a `sat` reply's model: `y_p` per flat place, zero where the model
1158
+ * is silent. `null` when the reply defines no `y`.
1159
+ */
1160
+ declare function decodeLinearBound(stdout: string, placeCount: number): bigint[] | null;
1161
+ /**
1162
+ * Re-proves the bound in exact integer arithmetic: `y ≥ 0`, zero on every H1/H3'
1163
+ * place, `y·C ≤ 0` on every flat transition, and `y·d ≥ y·M0 + 1`. Returns the bound
1164
+ * when every check passes and `null` otherwise — the verifier then continues to the
1165
+ * fixpoint query rather than trust the solver's model.
1166
+ */
1167
+ declare function checkLinearBoundExact(flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, y: readonly bigint[]): LinearBound | null;
1168
+ /** `2*a + b <= 2` — the bound as the report prints it. */
1169
+ declare function formatLinearBound(flatNet: FlatNet, bound: LinearBound): string;
1170
+ /** `ready_0 + ready_1 + _halt >= 3` — the violation's weighted demand as the report prints it. */
1171
+ declare function formatLinearDemand(flatNet: FlatNet, property: SmtProperty, bound: LinearBound): string;
776
1172
 
777
1173
  /**
778
1174
  * @module counterexample-decoder
@@ -801,12 +1197,12 @@ interface DecodedTrace {
801
1197
  readonly note: string | null;
802
1198
  }
803
1199
  /** Decodes the states of a z3 reply; a note says so when none were found. */
804
- declare function decode(answer: string, flatNet: FlatNet): DecodedTrace;
1200
+ declare function decode(answer: string, flatNet: FlatNet, counterCount?: number): DecodedTrace;
805
1201
  /**
806
1202
  * Collects the ground `Reachable(...)` applications from a z3 refutation proof into
807
1203
  * a state set, in text order.
808
1204
  */
809
- declare function decodeStateSet(answer: string, flatNet: FlatNet): ReadonlySet<MarkingState>;
1205
+ declare function decodeStateSet(answer: string, flatNet: FlatNet, counterCount?: number): ReadonlySet<MarkingState>;
810
1206
 
811
1207
  /**
812
1208
  * @module abstract-replayer
@@ -907,7 +1303,7 @@ type ReplayOutcome = {
907
1303
  * search — non-dominated states only, the root included (see
908
1304
  * {@link ReplayOptions.nodeBudget}).
909
1305
  */
910
- declare function replayCounterexample(flatNet: FlatNet, initial: AbstractState, decodedStates: readonly AbstractState[], property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, options?: ReplayOptions): ReplayOutcome;
1306
+ declare function replayCounterexample(flatNet: FlatNet, initial: AbstractState, decodedStates: readonly AbstractState[], property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, options?: ReplayOptions, conditionalSinks?: readonly ConditionalSinks[]): ReplayOutcome;
911
1307
 
912
1308
  /**
913
1309
  * Difference Bound Matrix (DBM) for Time Petri Net state class analysis.
@@ -940,10 +1336,35 @@ declare class DBM {
940
1336
  * Implements the 5-step Berthomieu-Diaz successor formula.
941
1337
  */
942
1338
  fireTransition(firedClock: number, newClockNames: readonly string[], newLowerBounds: number[], newUpperBounds: number[], persistentClocks: number[]): DBM;
1339
+ /**
1340
+ * The same zone with its clocks reordered: clock `k` of the result is clock
1341
+ * `order[k]` of this DBM. `order` must be a permutation of `0..clockCount()-1`.
1342
+ *
1343
+ * The state-class graph applies this to put every class's clocks in the one
1344
+ * canonical order (VER-010), so two arrivals at the same marking and zone whose
1345
+ * transitions became enabled in a different sequence share a key instead of
1346
+ * being counted as two classes. The reference row and column stay put; the
1347
+ * matrix is copied once, O(dim²) against the O(dim³) canonicalisation every
1348
+ * successor already pays.
1349
+ */
1350
+ permuted(order: readonly number[]): DBM;
943
1351
  /** Lets time pass: set all lower bounds to 0. */
944
1352
  letTimePass(): DBM;
945
1353
  private canonicalize;
946
1354
  equals(other: DBM): boolean;
1355
+ /**
1356
+ * The zone's identity for state-class dedup: the clock names and the FULL
1357
+ * canonical matrix, every difference bound included.
1358
+ *
1359
+ * {@link toString} prints only the per-clock projections `[lo, hi]`, and two
1360
+ * zones can agree on every projection while disagreeing on a difference
1361
+ * constraint `θi - θj <= c` — the class where one transition must fire no later
1362
+ * than another versus the class where either may go first. Keying on the
1363
+ * projections merges those, and since the graph explores only the first
1364
+ * arrival's successors, a marking reachable only from the second is lost: a
1365
+ * false `proven`. This key is what {@link equals} compares, rendered.
1366
+ */
1367
+ zoneKey(): string;
947
1368
  toString(): string;
948
1369
  }
949
1370
 
@@ -1098,4 +1519,4 @@ declare class TimePetriNetAnalyzerBuilder {
1098
1519
  build(): TimePetriNetAnalyzer;
1099
1520
  }
1100
1521
 
1101
- export { type AbstractState, EnvironmentAnalysisMode as AnalysisEnvironmentMode, type BranchEdge, type CertificateCheckOutcome, type CertificateVc, DBM, DUMP_ENV, type DecodedTrace, type EncodedScripts, EnvironmentAnalysisMode, type FlatNet, type FlatTransition, IncidenceMatrix, type LivenessResult, MIN_Z3_VERSION, MarkingState, MarkingStateBuilder, PInvariant, type PrioritySemantics, type QueryProven, type QueryResult, type QueryUnknown, type QueryViolated, type ReplayOptions, type ReplayOutcome, type ReplayStep, type SmtEncoding, SmtProperty, SmtVerificationResult, SmtVerifier, StateClass, StateClassGraph, type StructuralCheckResult, TimePetriNetAnalyzer, TimePetriNetAnalyzerBuilder, type XorBranchAnalysis, type XorBranchInfo, type Z3Exit, Z3ProcessError, type Z3Reply, type Z3Solver, Z3Unavailable, type Z3Version, Z3_ENV, canonicalInvariantOrder, checkCertificate, computePInvariants, computePSemiflows, computeSCCs, decode, decodeStateSet, encode, encodeStepRelationSmt2, findMaximalTrapIn, findMinimalSiphons, findTerminalSCCs, flatNetIndexOf, flatNetPlaceCount, flatNetTransitionCount, flatTransition, flatten, formatZ3Version, isCoveredByInvariants, parseZ3Version, placeholderCertificate, replayCounterexample, resolveZ3, runZ3Spacer, runZ3Text, strengthenWithSemiflows, structuralCheck, vcScript, z3Available, z3SolverAt };
1522
+ export { type AbstractState, EnvironmentAnalysisMode as AnalysisEnvironmentMode, type BranchEdge, type CertificateCheckOutcome, type CertificateVc, type ClassView, type ConditionalSinks, DBM, DUMP_ENV, type DecodedTrace, type EncodeOptions, type EncodedScripts, EnvironmentAnalysisMode, type FlatNet, type FlatTransition, IncidenceMatrix, type LinearBound, type LivenessResult, MIN_Z3_VERSION, MarkingState, MarkingStateBuilder, NOTE_ENUMERATED, PInvariant, type PrioritySemantics, type QueryProven, type QueryResult, type QueryUnknown, type QueryViolated, type ReplayOptions, type ReplayOutcome, type ReplayStep, type ScgOutcome, type SmtEncoding, SmtProperty, SmtVerificationResult, SmtVerifier, StateClass, StateClassGraph, type StructuralCheckResult, TimePetriNetAnalyzer, TimePetriNetAnalyzerBuilder, Verdict, type XorBranchAnalysis, type XorBranchInfo, type Z3Exit, Z3ProcessError, type Z3Reply, type Z3Solver, Z3Unavailable, type Z3Version, Z3_ENV, canonicalInvariantOrder, checkCertificate, checkLinearBoundExact, computePInvariants, computePSemiflows, computeSCCs, decideOverClasses, decode, decodeLinearBound, decodeStateSet, describeSinks, encode, encodeLinearBound, encodeNet, encodeStepRelationSmt2, findMaximalTrapIn, findMinimalSiphons, findTerminalSCCs, flatNetIndexOf, flatNetPlaceCount, flatNetTransitionCount, flatTransition, flatten, formatLinearBound, formatLinearDemand, formatZ3Version, isCoveredByInvariants, isUntimed, parseZ3Version, placeholderCertificate, replayCounterexample, resolveZ3, rethrowIfProgrammingError, runZ3Spacer, runZ3Text, strandingExcuses, strandsToken, strengthenWithSemiflows, structuralCheck, vcScript, verifyViaStateClassGraph, violationDemand, z3Available, z3SolverAt };