libpetri 2.7.0 → 2.8.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.
@@ -157,6 +157,85 @@ declare function requiredCount(spec: In): number;
157
157
  */
158
158
  declare function consumptionCount(spec: In, available: number): number;
159
159
 
160
+ /**
161
+ * ν-net token identities ([[NameId]]).
162
+ *
163
+ * A {@link NameId} is an **opaque correlation identity** carried by a token so
164
+ * that a fork can mint a fresh name and a later join can re-merge exactly the
165
+ * siblings that share it (see {@link import('./match-spec.js').MatchSpec}). The
166
+ * only operation defined on names is **equality** (`===`); names also have a
167
+ * lexicographic order used purely for the deterministic match tie-break
168
+ * (NU-020).
169
+ *
170
+ * Identity is a *projection* of the token payload, not a field on
171
+ * {@link import('./token.js').Token}: a `MatchSpec` declares the
172
+ * `value → NameId` key, and the fork mints a fresh name into the payload via
173
+ * `ctx.freshName()`. This keeps `Token` (and the event/archive formats)
174
+ * unchanged while still giving the analyzer a name-symmetric uninterpreted sort
175
+ * to reason about (spec NU-001).
176
+ */
177
+ /** An opaque correlation identity for ν-net fork/join (a branded string). */
178
+ type NameId = string & {
179
+ readonly __nameIdBrand: unique symbol;
180
+ };
181
+ /** Creates a {@link NameId} from a string. */
182
+ declare function nameId(value: string): NameId;
183
+
184
+ /**
185
+ * ν-net join correlation ([[MatchSpec]]).
186
+ *
187
+ * A {@link MatchSpec} declares that a subset of a transition's **input** places
188
+ * must be correlated by **name equality**: the transition is enabled only when
189
+ * there exists a single {@link NameId} `n` such that every correlated input
190
+ * supplies (at least) its required token count whose projected name equals `n`.
191
+ * On firing, exactly those name-matched tokens are consumed (spec NU-020).
192
+ *
193
+ * This is the single *decidable* predicate — equality of opaque names — and is
194
+ * deliberately NOT a general guard: it correlates the name dimension *across*
195
+ * places (composition-structural, like cardinality) rather than evaluating an
196
+ * arbitrary boolean per token. When both a unary input `guard` and a match are
197
+ * present the guard filters first, then the name correlation runs over the
198
+ * survivors (NU-021).
199
+ */
200
+
201
+ /**
202
+ * A name projection: maps a token's value to its {@link NameId}. A projection
203
+ * that yields no name (returns `null`/`undefined` at runtime) is treated as
204
+ * "no name" — that token never correlates — mirroring the Java/Rust `KeyFn`
205
+ * contract; the binding selector handles a nullish result defensively.
206
+ */
207
+ type KeyFn<T = any> = (value: T) => NameId;
208
+ /** One correlated input: the place plus its name projection. */
209
+ interface MatchKey<T = any> {
210
+ readonly place: Place<T>;
211
+ readonly key: KeyFn<T>;
212
+ }
213
+ /** Correlated fork/join match specification (ν-net join side). */
214
+ interface MatchSpec {
215
+ readonly keys: readonly MatchKey[];
216
+ }
217
+ /** Builds one correlated input for a {@link MatchSpec}. */
218
+ declare function matchKey<T>(place: Place<T>, key: KeyFn<T>): MatchKey<T>;
219
+ /**
220
+ * Builds a {@link MatchSpec} from two or more correlated inputs.
221
+ *
222
+ * @example
223
+ * Transition.builder('join')
224
+ * .inputs(one(branchA), one(branchB))
225
+ * .match(matchSpec(
226
+ * matchKey(branchA, (m: Msg) => nameId(m.correlationId)),
227
+ * matchKey(branchB, (m: Msg) => nameId(m.correlationId)),
228
+ * ))
229
+ *
230
+ * @throws if fewer than two inputs are correlated (a match over a single place
231
+ * is just a guard).
232
+ */
233
+ declare function matchSpec(...keys: MatchKey[]): MatchSpec;
234
+ /** Returns the name projection for `placeName`, or `undefined` if not correlated. */
235
+ declare function keyForPlace(spec: MatchSpec, placeName: string): KeyFn | undefined;
236
+ /** True when `placeName` is one of the correlated inputs. */
237
+ declare function matchCorrelates(spec: MatchSpec, placeName: string): boolean;
238
+
160
239
  /**
161
240
  * Output specification with explicit split semantics.
162
241
  * Supports composite structures (XOR of ANDs, AND of XORs, etc.)
@@ -364,6 +443,7 @@ declare class TransitionContext {
364
443
  private readonly executionCtx;
365
444
  private readonly _logFn?;
366
445
  private readonly placeAlias;
446
+ private _freshNameSupplier?;
367
447
  constructor(transitionName: string, rawInput: TokenInput, rawOutput: TokenOutput, inputPlaces: ReadonlySet<Place<any>>, readPlaces: ReadonlySet<Place<any>>, outputPlaces: ReadonlySet<Place<any>>, executionContext?: Map<string, unknown>, logFn?: LogFn, placeAlias?: ReadonlyMap<string, Place<any>>);
368
448
  /**
369
449
  * Resolves a place key through the transition's declared→actual place
@@ -416,6 +496,22 @@ declare class TransitionContext {
416
496
  /** Returns declared output places. */
417
497
  outputPlaces(): ReadonlySet<Place<any>>;
418
498
  private requireOutput;
499
+ /**
500
+ * @internal Installs the ν-name minter. Wired by the executor at firing time
501
+ * so names minted by {@link freshName} are monotonic across the run and
502
+ * instance-prefixed (spec NU-010, NU-030).
503
+ */
504
+ setFreshNameSupplier(supplier: () => NameId): void;
505
+ /**
506
+ * Mints a fresh ν-name (the ν-binder primitive — spec NU-010).
507
+ *
508
+ * An action calls this on the fork side to create a correlation id, then
509
+ * writes it into the sibling output payloads; a later join correlates those
510
+ * siblings via a {@link import('./match-spec.js').MatchSpec}. Uses the
511
+ * executor-installed minter when present; otherwise falls back to a
512
+ * process-global counter prefixed by the transition name.
513
+ */
514
+ freshName(): NameId;
419
515
  /** Returns the transition name. */
420
516
  transitionName(): string;
421
517
  /** Retrieves an execution context object by key. */
@@ -503,6 +599,11 @@ declare class Transition {
503
599
  readonly actionTimeout: OutTimeout | null;
504
600
  readonly action: TransitionAction;
505
601
  readonly priority: number;
602
+ /**
603
+ * ν-net join correlation: a subset of `inputSpecs` that must be correlated by
604
+ * name equality on firing (spec NU-020). `null` for ordinary transitions.
605
+ */
606
+ readonly matchSpec: MatchSpec | null;
506
607
  /**
507
608
  * Per-transition **declared → actual** place correspondence (per
508
609
  * **MOD-031**), keyed by the author-original declared place **name** →
@@ -519,7 +620,7 @@ declare class Transition {
519
620
  private readonly _readPlaces;
520
621
  private readonly _outputPlaces;
521
622
  /** @internal Use {@link Transition.builder} to create instances. */
522
- constructor(key: symbol, name: string, inputSpecs: readonly In[], outputSpec: Out | null, inhibitors: readonly ArcInhibitor[], reads: readonly ArcRead[], resets: readonly ArcReset[], timing: Timing, action: TransitionAction, priority: number, placeAlias?: ReadonlyMap<string, Place<any>>);
623
+ constructor(key: symbol, name: string, inputSpecs: readonly In[], outputSpec: Out | null, inhibitors: readonly ArcInhibitor[], reads: readonly ArcRead[], resets: readonly ArcReset[], timing: Timing, action: TransitionAction, priority: number, placeAlias?: ReadonlyMap<string, Place<any>>, matchSpec?: MatchSpec | null);
523
624
  /** Returns set of input places — consumed tokens. */
524
625
  inputPlaces(): ReadonlySet<Place<any>>;
525
626
  /** Returns set of read places — context tokens, not consumed. */
@@ -542,6 +643,7 @@ declare class TransitionBuilder {
542
643
  private _action;
543
644
  private _priority;
544
645
  private _placeAlias;
646
+ private _matchSpec;
545
647
  constructor(name: string);
546
648
  /** Add input specifications with cardinality. */
547
649
  inputs(...specs: In[]): this;
@@ -565,6 +667,12 @@ declare class TransitionBuilder {
565
667
  action(action: TransitionAction): this;
566
668
  /** Set the priority (higher fires first). */
567
669
  priority(priority: number): this;
670
+ /**
671
+ * Sets the ν-net join correlation spec: the named input places must be
672
+ * correlated by name equality on firing (spec NU-020). Every place referenced
673
+ * by the spec must also be declared as an input.
674
+ */
675
+ match(spec: MatchSpec): this;
568
676
  /**
569
677
  * Sets the per-transition declared→actual place correspondence (per
570
678
  * **MOD-031**). Populated by the subnet rewriter during the compose-time
@@ -672,7 +780,7 @@ declare class InterfaceBuilder {
672
780
  * violates the property, Spacer finds a counterexample. If no violation
673
781
  * is reachable, the property is proven.
674
782
  */
675
- type SmtProperty = DeadlockFree | MutualExclusion | PlaceBound | Unreachable;
783
+ type SmtProperty = DeadlockFree | MutualExclusion | PlaceBound | Unreachable | BranchPlaceBound | JoinedOrDeadLettered;
676
784
  /** Deadlock-freedom: no reachable marking has all transitions disabled. */
677
785
  interface DeadlockFree {
678
786
  readonly type: 'deadlock-free';
@@ -694,10 +802,39 @@ interface Unreachable {
694
802
  readonly type: 'unreachable';
695
803
  readonly places: ReadonlySet<Place<any>>;
696
804
  }
805
+ /**
806
+ * Branch / budget place bound: a ν-net budget or fork-branch place never
807
+ * exceeds `bound` tokens — the bounded-budget decidability lever (NU-040).
808
+ *
809
+ * Encodes identically to {@link PlaceBound} (a linear-integer count bound), but
810
+ * names the ν-net intent: the live correlation pool is bounded, keeping the
811
+ * well-structured transition system finite. The matched-transition
812
+ * over-approximation is sound for this safety bound — a `proven` verdict holds
813
+ * for the real net, which fires strictly fewer joins than the over-approximation.
814
+ */
815
+ interface BranchPlaceBound {
816
+ readonly type: 'branch-place-bound';
817
+ readonly place: Place<any>;
818
+ readonly bound: number;
819
+ }
820
+ /**
821
+ * Joined-or-dead-lettered: every forked name is eventually joined or
822
+ * dead-lettered, so no reachable *quiescent* (deadlocked) marking still holds a
823
+ * token in `pending` (NU-040). Violated when a reachable marking is both
824
+ * quiescent and has `pending >= 1` — a stranded correlation group.
825
+ */
826
+ interface JoinedOrDeadLettered {
827
+ readonly type: 'joined-or-dead-lettered';
828
+ readonly pending: Place<any>;
829
+ }
697
830
  declare function deadlockFree(): DeadlockFree;
698
831
  declare function mutualExclusion(p1: Place<any>, p2: Place<any>): MutualExclusion;
699
832
  declare function placeBound(place: Place<any>, bound: number): PlaceBound;
700
833
  declare function unreachable(places: ReadonlySet<Place<any>>): Unreachable;
834
+ /** Branch / budget place bound (NU-040). See {@link BranchPlaceBound}. */
835
+ declare function branchPlaceBound(place: Place<any>, bound: number): BranchPlaceBound;
836
+ /** Joined-or-dead-lettered at quiescence (NU-040). See {@link JoinedOrDeadLettered}. */
837
+ declare function joinedOrDeadLettered(pending: Place<any>): JoinedOrDeadLettered;
701
838
  /** Human-readable description of a property. */
702
839
  declare function propertyDescription(prop: SmtProperty): string;
703
840
 
@@ -1591,4 +1728,4 @@ declare class PetriNetBuilder {
1591
1728
  private buildWithFusion;
1592
1729
  }
1593
1730
 
1594
- export { and as $, type Arc as A, SubnetDefBuilder as B, type Channel as C, type SubnetInstance as D, type EnvironmentPlace as E, FusionSet as F, type Timing as G, type TimingDeadline as H, type In as I, type TimingDelayed as J, type TimingExact as K, type LogFn as L, MAX_DURATION_MS as M, type TimingImmediate as N, type Out as O, PetriNet as P, type TimingWindow as Q, TokenInput as R, SubnetDef as S, type Token as T, TokenOutput as U, type TransitionAction as V, TransitionBuilder as W, type VerificationHarness as X, type VerificationResult as Y, all as Z, allPlaces as _, type Place as a, placeBound as a$, andPlaces as a0, arcPlace as a1, atLeast as a2, consumptionCount as a3, deadline as a4, delayed as a5, earliest as a6, enumerateBranches as a7, environmentPlace as a8, exact as a9, transformFrom as aA, unitToken as aB, window as aC, withTimeout as aD, xor as aE, xorPlaces as aF, MarkingState as aG, type PInvariant as aH, MarkingStateBuilder as aI, type SmtProperty as aJ, type SmtVerificationResult as aK, type DeadlockFree as aL, type MutualExclusion as aM, type PlaceBound as aN, type Proven as aO, type SmtStatistics as aP, type TokenSupplier as aQ, type Unknown as aR, type Unreachable as aS, type Verdict as aT, type Violated as aU, deadlockFree as aV, isProven as aW, isViolated as aX, mutualExclusion as aY, pInvariant as aZ, pInvariantToString as a_, exactly as aa, fork as ab, forwardInput as ac, hasDeadline as ad, hasGuard as ae, immediate as af, inhibitorArc as ag, inputArc as ah, isUnit as ai, latest as aj, matchesGuard as ak, one as al, outPlace as am, outputArc as an, passthrough as ao, place as ap, produce as aq, readArc as ar, requiredCount as as, resetArc as at, timeout as au, timeoutPlace as av, tokenAt as aw, tokenOf as ax, transform as ay, transformAsync as az, Transition as b, propertyDescription as b0, unreachable as b1, TransitionContext as c, type ArcInhibitor as d, type ArcInput as e, type ArcOutput as f, type ArcRead as g, type ArcReset as h, ComposeBindings as i, FusionSetBuilder as j, type InAll as k, type InAtLeast as l, type InExactly as m, type InOne as n, Instance as o, Interface as p, InterfaceBuilder as q, type OutAnd as r, type OutForwardInput as s, type OutPlace as t, type OutTimeout as u, type OutXor as v, type OutputEntry as w, PetriNetBuilder as x, type Port as y, type PortDirection as z };
1731
+ export { type VerificationHarness as $, type Arc as A, type Port as B, type Channel as C, type PortDirection as D, type EnvironmentPlace as E, FusionSet as F, SubnetDefBuilder as G, type SubnetInstance as H, type In as I, type Timing as J, type KeyFn as K, type LogFn as L, MAX_DURATION_MS as M, type NameId as N, type Out as O, PetriNet as P, type TimingDeadline as Q, type TimingDelayed as R, SubnetDef as S, type Token as T, type TimingExact as U, type TimingImmediate as V, type TimingWindow as W, TokenInput as X, TokenOutput as Y, type TransitionAction as Z, TransitionBuilder as _, type Place as a, type TokenSupplier as a$, type VerificationResult as a0, all as a1, allPlaces as a2, and as a3, andPlaces as a4, arcPlace as a5, atLeast as a6, consumptionCount as a7, deadline as a8, delayed as a9, readArc as aA, requiredCount as aB, resetArc as aC, timeout as aD, timeoutPlace as aE, tokenAt as aF, tokenOf as aG, transform as aH, transformAsync as aI, transformFrom as aJ, unitToken as aK, window as aL, withTimeout as aM, xor as aN, xorPlaces as aO, MarkingState as aP, type PInvariant as aQ, MarkingStateBuilder as aR, type SmtProperty as aS, type SmtVerificationResult as aT, type BranchPlaceBound as aU, type DeadlockFree as aV, type JoinedOrDeadLettered as aW, type MutualExclusion as aX, type PlaceBound as aY, type Proven as aZ, type SmtStatistics as a_, earliest as aa, enumerateBranches as ab, environmentPlace as ac, exact as ad, exactly as ae, fork as af, forwardInput as ag, hasDeadline as ah, hasGuard as ai, immediate as aj, inhibitorArc as ak, inputArc as al, isUnit as am, keyForPlace as an, latest as ao, matchCorrelates as ap, matchKey as aq, matchSpec as ar, matchesGuard as as, nameId as at, one as au, outPlace as av, outputArc as aw, passthrough as ax, place as ay, produce as az, Transition as b, type Unknown as b0, type Unreachable as b1, type Verdict as b2, type Violated as b3, branchPlaceBound as b4, deadlockFree as b5, isProven as b6, isViolated as b7, joinedOrDeadLettered as b8, mutualExclusion as b9, pInvariant as ba, pInvariantToString as bb, placeBound as bc, propertyDescription as bd, unreachable as be, TransitionContext as c, type ArcInhibitor as d, type ArcInput as e, type ArcOutput as f, type ArcRead as g, type ArcReset as h, ComposeBindings as i, FusionSetBuilder as j, type InAll as k, type InAtLeast as l, type InExactly as m, type InOne as n, Instance as o, Interface as p, InterfaceBuilder as q, type MatchKey as r, type MatchSpec as s, type OutAnd as t, type OutForwardInput as u, type OutPlace as v, type OutTimeout as w, type OutXor as x, type OutputEntry as y, PetriNetBuilder as z };
@@ -1,5 +1,5 @@
1
- import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aG as MarkingState, aH as PInvariant, aI as MarkingStateBuilder, aJ as SmtProperty, aK as SmtVerificationResult } from '../petri-net-D73-PO6d.js';
2
- export { aL as DeadlockFree, aM as MutualExclusion, aN as PlaceBound, aO as Proven, aP as SmtStatistics, aQ as TokenSupplier, aR as Unknown, aS as Unreachable, aT as Verdict, X as VerificationHarness, Y as VerificationResult, aU as Violated, aV as deadlockFree, aW as isProven, aX as isViolated, aY as mutualExclusion, aZ as pInvariant, a_ as pInvariantToString, a$ as placeBound, b0 as propertyDescription, b1 as unreachable } from '../petri-net-D73-PO6d.js';
1
+ import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aP as MarkingState, aQ as PInvariant, aR as MarkingStateBuilder, aS as SmtProperty, aT as SmtVerificationResult } from '../petri-net-B4wQwsUj.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 branchPlaceBound, b5 as deadlockFree, b6 as isProven, b7 as isViolated, b8 as joinedOrDeadLettered, b9 as mutualExclusion, ba as pInvariant, bb as pInvariantToString, bc as placeBound, bd as propertyDescription, be as unreachable } from '../petri-net-B4wQwsUj.js';
3
3
  import { Expr, init, Bool, FuncDecl } from 'z3-solver';
4
4
 
5
5
  /**
@@ -275,8 +275,10 @@ declare class SmtVerifier {
275
275
  private _property;
276
276
  private readonly _environmentPlaces;
277
277
  private readonly _sinkPlaces;
278
+ private readonly _budgetPlaces;
278
279
  private _environmentMode;
279
280
  private _timeoutMs;
281
+ private _nuMaxClasses;
280
282
  private constructor();
281
283
  static forNet(net: PetriNet): SmtVerifier;
282
284
  initialMarking(marking: MarkingState): this;
@@ -289,11 +291,47 @@ declare class SmtVerifier {
289
291
  * Markings where any sink place has a token are not considered deadlocks.
290
292
  */
291
293
  sinkPlaces(...places: Place<any>[]): this;
294
+ /**
295
+ * Declares ν-net budget places (NU-040): places whose token count bounds the
296
+ * live correlation pool (they gate fresh-name minting). Declaring at least one
297
+ * places the net in the decidable bounded fragment, so reachability-safety
298
+ * properties over its ν-joins are verified (the matched transitions are
299
+ * over-approximated). Without any budget place, a net that mints fresh names
300
+ * is treated as unbounded and the verifier returns `unknown` (NU-050).
301
+ */
302
+ budgetPlaces(...places: Place<any>[]): this;
292
303
  timeout(ms: number): this;
304
+ /**
305
+ * Sets the class-count cap for the ν-aware state-class-graph analysis (NU-050,
306
+ * Route B). When the symbolic name-aware graph would exceed this, the analysis
307
+ * truncates and the verdict is `unknown` (the live correlation pool is not
308
+ * structurally bounded). Default 100_000.
309
+ */
310
+ nuMaxClasses(max: number): this;
293
311
  /**
294
312
  * Runs the verification pipeline.
295
313
  */
296
314
  verify(): Promise<SmtVerificationResult>;
315
+ /**
316
+ * ν-net soundness guard (NU-040, NU-050). Applied only when the net contains
317
+ * match (ν-join) transitions, and only to a proven/violated verdict (an
318
+ * existing unknown is left as-is).
319
+ *
320
+ * - Quiescence-based properties (deadlock / joined-or-dead-lettered): the
321
+ * name-blind over-approximation over-fires joins, so it sees fewer quiescent
322
+ * states and may miss a real stranded marking — downgraded to unknown
323
+ * (exact quiescence reasoning is deferred to the SCG name-partition quotient).
324
+ * - Reachability-safety with unbounded fresh names (no budget declared):
325
+ * reachability over unbounded fresh names is undecidable — unknown.
326
+ * - Bounded reachability-safety in the name-coloured fragment (`exact`): name
327
+ * equality is encoded exactly via bounded name-colouring, so the verdict is
328
+ * sound *and* complete within the budget — no spurious different-name
329
+ * counterexample. The verdict is kept and the exact-path note is appended.
330
+ * - Bounded reachability-safety outside that fragment: `proven` is sound; a
331
+ * `violated` may be spurious — the verdict is kept and the over-approximation
332
+ * caveat is appended to the report.
333
+ */
334
+ private applyNuGuard;
297
335
  }
298
336
 
299
337
  /**