libpetri 3.0.0 → 4.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.
- package/README.md +54 -2
- package/dist/chunk-KO6TSB47.js +1016 -0
- package/dist/chunk-KO6TSB47.js.map +1 -0
- package/dist/{chunk-5W6SVYPD.js → chunk-WYCGGQAW.js} +1744 -737
- package/dist/chunk-WYCGGQAW.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/doclet/index.d.ts +12 -3
- package/dist/doclet/index.js +5 -1
- package/dist/doclet/index.js.map +1 -1
- package/dist/doclet/resources/petrinet-diagrams.css +21 -0
- package/dist/doclet/resources/petrinet-diagrams.js +6366 -6360
- package/dist/{render-ZGZEZ5RK.js → elk-place-YVNQFGXI.js} +3 -258
- package/dist/elk-place-YVNQFGXI.js.map +1 -0
- package/dist/{event-store-Df_sAVQ_.d.ts → event-store-2FOAeUyh.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/pan-zoom-Cp51IkDl.d.ts +33 -0
- package/dist/{petri-net-UQBBkvLl.d.ts → petri-net-hduM6tJf.d.ts} +20 -1
- package/dist/preprocess-FN3F75JR.js +193 -0
- package/dist/preprocess-FN3F75JR.js.map +1 -0
- package/dist/render-QOHGDWNE.js +78 -0
- package/dist/render-QOHGDWNE.js.map +1 -0
- package/dist/render-dom/index.d.ts +28 -29
- package/dist/render-dom/index.js +14 -21
- package/dist/render-dom/index.js.map +1 -1
- package/dist/verification/index.d.ts +440 -71
- package/dist/verification/index.js +43 -5
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.d.ts +24 -32
- package/dist/viewer/index.js +9 -1003
- package/dist/viewer/index.js.map +1 -1
- package/dist/viewer/viewer.css +21 -0
- package/dist/viewer/viewer.iife.js +6366 -6360
- package/package.json +5 -8
- package/dist/chunk-5W6SVYPD.js.map +0 -1
- package/dist/render-ZGZEZ5RK.js.map +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aO as
|
|
2
|
-
export { aT as BranchPlaceBound, aU as DeadlockFree, aV as JoinedOrDeadLettered, aW as MutualExclusion, aX as PlaceBound, aY as Proven, aZ as SmtStatistics, a_ as TokenSupplier, a$ as Unknown, b0 as Unreachable, b1 as Verdict, $ as VerificationHarness, a0 as VerificationResult, b2 as Violated, b3 as branchPlaceBound, b4 as deadlockFree, b5 as isProven, b6 as isViolated, b7 as joinedOrDeadLettered, b8 as mutualExclusion, b9 as pInvariant, ba as pInvariantToString, bb as placeBound, bc as propertyDescription, bd as unreachable } from '../petri-net-
|
|
3
|
-
import { Expr, init, Bool, FuncDecl } from 'z3-solver';
|
|
1
|
+
import { b as Transition, a as Place, P as PetriNet, E as EnvironmentPlace, aO as PInvariant, aP as MarkingState, aQ as SmtProperty, aR as MarkingStateBuilder, aS as SmtVerificationResult } from '../petri-net-hduM6tJf.js';
|
|
2
|
+
export { aT as BranchPlaceBound, aU as DeadlockFree, aV as JoinedOrDeadLettered, aW as MutualExclusion, aX as PlaceBound, aY as Proven, aZ as SmtStatistics, a_ as TokenSupplier, a$ as Unknown, b0 as Unreachable, b1 as Verdict, $ as VerificationHarness, a0 as VerificationResult, b2 as Violated, b3 as branchPlaceBound, b4 as deadlockFree, b5 as isProven, b6 as isViolated, b7 as joinedOrDeadLettered, b8 as mutualExclusion, b9 as pInvariant, ba as pInvariantToString, bb as placeBound, bc as propertyDescription, bd as unreachable } from '../petri-net-hduM6tJf.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* A flattened transition with pre/post vectors for SMT encoding.
|
|
@@ -182,22 +181,35 @@ declare function flatten(net: PetriNet, environmentPlaces?: Set<EnvironmentPlace
|
|
|
182
181
|
*/
|
|
183
182
|
declare function computePInvariants(matrix: IncidenceMatrix, flatNet: FlatNet, initialMarking: MarkingState): PInvariant[];
|
|
184
183
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* a genuine P-semiflow, with `constant = y·M0`. A non-negative conservation law
|
|
189
|
-
* soundly **bounds** the token sum over its support: `Σ_{support} M(p) ≤ y·M0`. Used
|
|
190
|
-
* to bound the number of simultaneously-live colours in the name-coloured encoder
|
|
191
|
-
* (see `colourSlotBound`).
|
|
184
|
+
* VER-007 — the semiflow union. Appends every gate-validated P-semiflow that is not
|
|
185
|
+
* already a basis row (same weights, same constant) to `invariants`, returning the
|
|
186
|
+
* strengthened list and how many rows were added.
|
|
192
187
|
*
|
|
193
|
-
*
|
|
188
|
+
* The null-space basis is one basis of many: elimination hands back mixed-sign rows
|
|
189
|
+
* and rows that fold a reset place into a chain whose other combinations avoid it,
|
|
190
|
+
* both lost to the exact gate — on a reset-heavy net every law of the chains those
|
|
191
|
+
* arcs touch, leaving IC3 to rediscover conservation it cannot within any practical
|
|
192
|
+
* budget. The Farkas rows ({@link computePSemiflows}) are the minimal laws of the
|
|
193
|
+
* net. Conjoining them alongside the basis is pure strengthening (`Semiflow.lean`,
|
|
194
|
+
* `semiflow_union_sound`) **provided both lists passed the same exact gate**
|
|
195
|
+
* (`semiflow_gate_is_necessary`) — the caller's obligation; this only merges.
|
|
194
196
|
*/
|
|
197
|
+
declare function strengthenWithSemiflows(invariants: readonly PInvariant[], semiflows: readonly PInvariant[]): {
|
|
198
|
+
readonly invariants: readonly PInvariant[];
|
|
199
|
+
readonly added: number;
|
|
200
|
+
};
|
|
195
201
|
declare function computePSemiflows(matrix: IncidenceMatrix, flatNet: FlatNet, initialMarking: MarkingState): PInvariant[];
|
|
196
202
|
/**
|
|
197
203
|
* Checks if every place is covered by at least one P-invariant.
|
|
198
204
|
* If true, the net is structurally bounded.
|
|
199
205
|
*/
|
|
200
206
|
declare function isCoveredByInvariants(invariants: readonly PInvariant[], numPlaces: number): boolean;
|
|
207
|
+
/**
|
|
208
|
+
* The invariants in canonical order (VER-013): by ascending support, then weights,
|
|
209
|
+
* then constant, each compared lexicographically. The same order the Rust and Java
|
|
210
|
+
* verifiers apply, so the strengthened scripts are byte-identical.
|
|
211
|
+
*/
|
|
212
|
+
declare function canonicalInvariantOrder(invariants: readonly PInvariant[]): PInvariant[];
|
|
201
213
|
|
|
202
214
|
/**
|
|
203
215
|
* @module structural-check
|
|
@@ -259,6 +271,168 @@ declare function findMinimalSiphons(flatNet: FlatNet): ReadonlySet<number>[];
|
|
|
259
271
|
*/
|
|
260
272
|
declare function findMaximalTrapIn(flatNet: FlatNet, places: ReadonlySet<number>): ReadonlySet<number>;
|
|
261
273
|
|
|
274
|
+
/** Environment variable naming the z3 executable (default: `z3` on `PATH`). */
|
|
275
|
+
declare const Z3_ENV = "LIBPETRI_Z3";
|
|
276
|
+
/** Environment variable naming a directory that receives every script and reply. */
|
|
277
|
+
declare const DUMP_ENV = "LIBPETRI_SMT_DUMP";
|
|
278
|
+
/** A z3 release version, ordered numerically. */
|
|
279
|
+
interface Z3Version {
|
|
280
|
+
readonly major: number;
|
|
281
|
+
readonly minor: number;
|
|
282
|
+
readonly patch: number;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Oldest z3 the transport accepts: `-t`/`-T`, Spacer as `fp.engine`, and the
|
|
286
|
+
* `(get-model)` / `(get-proof)` printers the decoders read are stable from here.
|
|
287
|
+
*/
|
|
288
|
+
declare const MIN_Z3_VERSION: Z3Version;
|
|
289
|
+
/** Parses the version out of a `z3 --version` reply (`Z3 version 4.16.0 - 64 bit`). */
|
|
290
|
+
declare function parseZ3Version(text: string): Z3Version | null;
|
|
291
|
+
declare function formatZ3Version(v: Z3Version): string;
|
|
292
|
+
/** A resolved z3 executable: where it is and which version answered the probe. */
|
|
293
|
+
interface Z3Solver {
|
|
294
|
+
/** The executable as resolved (a path, or a bare name on `PATH`). */
|
|
295
|
+
readonly program: string;
|
|
296
|
+
/** The version the probe reported. */
|
|
297
|
+
readonly version: Z3Version;
|
|
298
|
+
/** Where scripts and replies are written, or `null` for no dump. */
|
|
299
|
+
readonly dumpDir: string | null;
|
|
300
|
+
}
|
|
301
|
+
/** No usable z3 resolved; the message is the `unknown` reason the verifier reports. */
|
|
302
|
+
declare class Z3Unavailable extends Error {
|
|
303
|
+
constructor(message: string);
|
|
304
|
+
}
|
|
305
|
+
/** The process could not be started; the message is the `unknown` reason. */
|
|
306
|
+
declare class Z3ProcessError extends Error {
|
|
307
|
+
constructor(message: string);
|
|
308
|
+
}
|
|
309
|
+
/** How a z3 process ended. */
|
|
310
|
+
type Z3Exit = {
|
|
311
|
+
readonly kind: 'exited';
|
|
312
|
+
readonly code: number | null;
|
|
313
|
+
} | {
|
|
314
|
+
readonly kind: 'killed';
|
|
315
|
+
};
|
|
316
|
+
/** The raw reply of one z3 run. */
|
|
317
|
+
interface Z3Reply {
|
|
318
|
+
readonly stdout: string;
|
|
319
|
+
readonly stderr: string;
|
|
320
|
+
readonly exit: Z3Exit;
|
|
321
|
+
}
|
|
322
|
+
/** Resolves a specific executable (tests point this at a stub). No dump directory. */
|
|
323
|
+
declare function z3SolverAt(program: string, env?: NodeJS.ProcessEnv): Z3Solver;
|
|
324
|
+
/**
|
|
325
|
+
* Resolves the executable named by {@link Z3_ENV}, or `z3` on `PATH`, probes its
|
|
326
|
+
* version, and reads {@link DUMP_ENV}. Throws {@link Z3Unavailable}.
|
|
327
|
+
*/
|
|
328
|
+
declare function resolveZ3(env?: NodeJS.ProcessEnv): Z3Solver;
|
|
329
|
+
/**
|
|
330
|
+
* True if a usable `z3` executable resolves: `LIBPETRI_Z3` if set, else `z3` on
|
|
331
|
+
* `PATH`, at or above {@link MIN_Z3_VERSION}. Without one every SMT path returns
|
|
332
|
+
* `unknown`; the test suites use this to skip loudly rather than fail.
|
|
333
|
+
*/
|
|
334
|
+
declare function z3Available(env?: NodeJS.ProcessEnv): boolean;
|
|
335
|
+
/**
|
|
336
|
+
* Runs one script through one z3 process and resolves with the raw reply. `phase`
|
|
337
|
+
* names the dump files; `extraArgs` follow the standard argument list. The only
|
|
338
|
+
* rejection is a failed spawn: a solver that printed nothing, errored, timed out or
|
|
339
|
+
* was killed still comes back as a reply for the caller to classify
|
|
340
|
+
* ({@link failureReason}).
|
|
341
|
+
*/
|
|
342
|
+
declare function runZ3Text(solver: Z3Solver, script: string, phase: string, timeoutMs: number, extraArgs?: readonly string[]): Promise<Z3Reply>;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @module certificate-checker
|
|
346
|
+
*
|
|
347
|
+
* Independent certificate check for IC3/PDR proofs.
|
|
348
|
+
*
|
|
349
|
+
* When Z3 Spacer answers `sat` on the CHC encoding ({@link module:smt-encoder}), the
|
|
350
|
+
* model it prints interprets `Reachable` as an inductive invariant, the proof
|
|
351
|
+
* certificate. This module re-verifies that certificate with plain (non-HORN) SMT
|
|
352
|
+
* queries in a SECOND z3 run, so a `proven` verdict no longer rests on the empirical
|
|
353
|
+
* HORN sat ⇒ proven mapping alone, nor on the correctness of the P-invariant
|
|
354
|
+
* strengthening: the three verification conditions below are discharged against the
|
|
355
|
+
* UNSTRENGTHENED step relation ({@link encodeStepRelationSmt2}).
|
|
356
|
+
*
|
|
357
|
+
* The candidate invariant is `R' := R ∧ Inv`, where `R` is the pasted `Reachable`
|
|
358
|
+
* interpretation and `Inv` the validated P-invariant equalities the CHC encoding
|
|
359
|
+
* strengthened its rule bodies with: a Spacer model is only guaranteed inductive
|
|
360
|
+
* *relative to* that strengthening, so the conjuncts ride along in the candidate, but
|
|
361
|
+
* the RELATION stays unstrengthened, which means VC1/VC2 re-prove each conjunct's
|
|
362
|
+
* initiation and inductiveness from scratch. A wrong P-invariant cannot weaken this
|
|
363
|
+
* check: it fails init or consecution instead.
|
|
364
|
+
*
|
|
365
|
+
* 1. **VC1 (init)**: `¬R'(M₀)` is UNSAT.
|
|
366
|
+
* 2. **VC2 (consecution)**: `M ≥ 0 ∧ R'(M) ∧ T(M,M') ∧ ¬R'(M')` is UNSAT.
|
|
367
|
+
* 3. **VC3 (safety)**: `M ≥ 0 ∧ R'(M) ∧ Bad(M)` is UNSAT.
|
|
368
|
+
*
|
|
369
|
+
* The `M ≥ 0` conjunct is the state domain: markings are token counts, so the VCs
|
|
370
|
+
* range over ℕ^P; without it a certificate inductive over ℕ^P is refuted by a negative
|
|
371
|
+
* predecessor in ℤ^P.
|
|
372
|
+
*
|
|
373
|
+
* The certificate is the `(define-fun …)` block of the `(get-model)` reply, pasted
|
|
374
|
+
* verbatim: auxiliary definitions stay alongside `Reachable`, so every name resolves
|
|
375
|
+
* in the fresh script. The three VCs run under `(push)`/`(pop)` in ONE script; the
|
|
376
|
+
* emitted text is byte-identical to the Rust reference (`certificate_check.rs`) and
|
|
377
|
+
* the Java port.
|
|
378
|
+
*
|
|
379
|
+
* Outcomes are split the way the caller must treat them: `failed` names the first VC
|
|
380
|
+
* that was not UNSAT (with the solver status and, for SAT, a witness marking),
|
|
381
|
+
* `unavailable` means the check could not run at all (missing or malformed
|
|
382
|
+
* certificate, solver spawn failure, errored assert). Both withhold PROVEN; neither
|
|
383
|
+
* throws.
|
|
384
|
+
*/
|
|
385
|
+
|
|
386
|
+
/** Label of a validity condition, as it appears in the downgrade reason. */
|
|
387
|
+
type CertificateVc = 'initiation (VC1)' | 'consecution (VC2)' | 'safety (VC3)';
|
|
388
|
+
/**
|
|
389
|
+
* Outcome of the certificate check.
|
|
390
|
+
*
|
|
391
|
+
* `passed` — all three validity conditions are UNSAT; the proven verdict is certified
|
|
392
|
+
* independently of the Fixedpoint engine.
|
|
393
|
+
* `failed` — a validity condition was not UNSAT; `detail` carries the solver status
|
|
394
|
+
* and, when the solver produced a model, a witness marking.
|
|
395
|
+
* `unavailable` — the check could not run (missing/malformed certificate, solver
|
|
396
|
+
* failure), so no VC is implicated.
|
|
397
|
+
*
|
|
398
|
+
* The caller must withhold PROVEN on `failed` and `unavailable` alike.
|
|
399
|
+
*/
|
|
400
|
+
type CertificateCheckOutcome = {
|
|
401
|
+
readonly type: 'passed';
|
|
402
|
+
readonly invariant: string;
|
|
403
|
+
} | {
|
|
404
|
+
readonly type: 'failed';
|
|
405
|
+
readonly vc: CertificateVc;
|
|
406
|
+
readonly detail: string;
|
|
407
|
+
readonly invariant: string;
|
|
408
|
+
} | {
|
|
409
|
+
readonly type: 'unavailable';
|
|
410
|
+
readonly reason: string;
|
|
411
|
+
readonly invariant: string | null;
|
|
412
|
+
};
|
|
413
|
+
/**
|
|
414
|
+
* Re-verifies an extracted proof certificate against the unstrengthened step relation.
|
|
415
|
+
*
|
|
416
|
+
* @param certificate the `(define-fun …)` block extracted verbatim from the Spacer
|
|
417
|
+
* model (`null` when the solver printed none)
|
|
418
|
+
* @param flatNet the flat net the CHC query was encoded from
|
|
419
|
+
* @param initialMarking the verified initial marking (VC1)
|
|
420
|
+
* @param property the verified property (VC3)
|
|
421
|
+
* @param invariants the exactly-validated P-invariants the CHC bodies were
|
|
422
|
+
* strengthened with; conjoined into the CANDIDATE certificate and re-proven by the
|
|
423
|
+
* three VCs (never conjoined into the step relation)
|
|
424
|
+
* @param sinkPlaces declared sink places (deadlock-freedom VC3)
|
|
425
|
+
* @param solver the resolved z3 executable
|
|
426
|
+
* @param timeoutMs per-invocation solver budget in milliseconds
|
|
427
|
+
*/
|
|
428
|
+
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>;
|
|
429
|
+
/**
|
|
430
|
+
* The certificate-check script for the given inputs, exactly as
|
|
431
|
+
* {@link checkCertificate} would send it (VER-013 script parity): what the
|
|
432
|
+
* cross-language golden tests diff.
|
|
433
|
+
*/
|
|
434
|
+
declare function vcScript(certificate: string, flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, invariants: readonly PInvariant[]): string;
|
|
435
|
+
|
|
262
436
|
/**
|
|
263
437
|
* Name-correlation fragment classifier for the ν-aware state class graph
|
|
264
438
|
* (NU-050, Route B). See the Rust/Java equivalents for the full contract.
|
|
@@ -362,6 +536,9 @@ declare class SmtVerifier {
|
|
|
362
536
|
private readonly _budgetPlaces;
|
|
363
537
|
private _environmentMode;
|
|
364
538
|
private _timeoutMs;
|
|
539
|
+
private _certificateCheck;
|
|
540
|
+
private _counterexampleReplay;
|
|
541
|
+
private _semiflowInvariants;
|
|
365
542
|
private _nuMaxClasses;
|
|
366
543
|
private _fragmentMode;
|
|
367
544
|
private readonly _carrierPlaces;
|
|
@@ -388,6 +565,49 @@ declare class SmtVerifier {
|
|
|
388
565
|
*/
|
|
389
566
|
budgetPlaces(...places: Place<any>[]): this;
|
|
390
567
|
timeout(ms: number): this;
|
|
568
|
+
/**
|
|
569
|
+
* Enables/disables the independent IC3 certificate check (default: enabled).
|
|
570
|
+
*
|
|
571
|
+
* When a proven verdict comes from the IC3/Spacer path on the flat count
|
|
572
|
+
* encoding, the synthesized inductive invariant is re-validated with a plain
|
|
573
|
+
* solver against the UNSTRENGTHENED step relation — VC1 (init), VC2
|
|
574
|
+
* (consecution), VC3 (safety) — so a Spacer or encoder defect cannot certify
|
|
575
|
+
* a false PROVEN. A certificate that fails validation downgrades the verdict
|
|
576
|
+
* to unknown. Structural proofs and the coloured ν-encoding are unaffected.
|
|
577
|
+
*/
|
|
578
|
+
certificateCheck(enabled: boolean): this;
|
|
579
|
+
/**
|
|
580
|
+
* Enables/disables abstract counterexample replay (default: enabled).
|
|
581
|
+
*
|
|
582
|
+
* When a violated verdict comes from the flat count encoding, the decoded
|
|
583
|
+
* counterexample states (an order-free set — the derivation tree is walked in
|
|
584
|
+
* traversal order, not firing order) are re-executed TS-side against the
|
|
585
|
+
* abstract semantics the encoder emits (Lean's `fireA`, Basic.lean), searching
|
|
586
|
+
* for a firing order from M₀ to a property-violating marking. See
|
|
587
|
+
* `SmtVerificationResult.counterexampleConfirmed` for how each outcome lands.
|
|
588
|
+
*/
|
|
589
|
+
counterexampleReplay(enabled: boolean): this;
|
|
590
|
+
/**
|
|
591
|
+
* Also hands the validated **P-semiflows** to the encoders as invariants
|
|
592
|
+
* (VER-007; default: disabled — the encoders then see only the null-space basis).
|
|
593
|
+
*
|
|
594
|
+
* Every validated semiflow is a conservation law in its own right (`y >= 0`,
|
|
595
|
+
* `y·C = 0`, `y·M0` exact, zero weight on every reset / consume-all place), and
|
|
596
|
+
* the Farkas enumeration returns the *minimal* laws of the net. The null-space
|
|
597
|
+
* basis the encoders get by default is one basis of many: elimination hands back
|
|
598
|
+
* mixed-sign rows (discarded as not semi-positive) or rows that fold a reset place
|
|
599
|
+
* into a chain whose other combinations avoid it (dropped by the H1 guard). On a
|
|
600
|
+
* net with a few reset arcs that can lose every law of the chains those arcs
|
|
601
|
+
* touch, and without them IC3 has to rediscover the conservation of each chain —
|
|
602
|
+
* on a ~100-place net it does not within any practical budget. With the semiflows
|
|
603
|
+
* in, the same reachability-safety queries close in about a second.
|
|
604
|
+
*
|
|
605
|
+
* Soundness is unchanged: the semiflows pass the same exact re-validation as the
|
|
606
|
+
* basis rows, the union is pure strengthening (`Semiflow.lean`,
|
|
607
|
+
* `semiflow_union_sound`), and the certificate check re-proves the strengthened
|
|
608
|
+
* invariant. Off by default so reports stay byte-equal.
|
|
609
|
+
*/
|
|
610
|
+
semiflowInvariants(enabled: boolean): this;
|
|
391
611
|
/**
|
|
392
612
|
* Sets the class-count cap for the ν-aware state-class-graph analysis (NU-050,
|
|
393
613
|
* Route B). When the symbolic name-aware graph would exceed this, the analysis
|
|
@@ -425,6 +645,16 @@ declare class SmtVerifier {
|
|
|
425
645
|
* dead-letter-drain stalls the eager, priority-ordered executor never produces.
|
|
426
646
|
*/
|
|
427
647
|
prioritySemantics(semantics: PrioritySemantics): this;
|
|
648
|
+
/**
|
|
649
|
+
* The SMT-LIB2 scripts {@link verify} would send to z3 for this configuration,
|
|
650
|
+
* without running a solver (VER-013 AC1): the HORN query (flat, or name-coloured
|
|
651
|
+
* when a declared budget puts the net on Route A's exact encoding) and, for the
|
|
652
|
+
* flat encoding, the certificate-check script built around
|
|
653
|
+
* {@link placeholderCertificate}. This is what the cross-language golden tests diff
|
|
654
|
+
* byte for byte. Route B, the structural pre-check and the unresolved-place
|
|
655
|
+
* refusal are bypassed: it is what Route A encodes.
|
|
656
|
+
*/
|
|
657
|
+
encodeScripts(): EncodedScripts;
|
|
428
658
|
/**
|
|
429
659
|
* Runs the verification pipeline.
|
|
430
660
|
*
|
|
@@ -452,104 +682,243 @@ declare class SmtVerifier {
|
|
|
452
682
|
*/
|
|
453
683
|
private applyNuGuard;
|
|
454
684
|
}
|
|
685
|
+
/** The scripts {@link SmtVerifier.encodeScripts} reports. */
|
|
686
|
+
interface EncodedScripts {
|
|
687
|
+
/** The HORN query, flat or name-coloured. */
|
|
688
|
+
readonly horn: string;
|
|
689
|
+
/** The certificate-check script around {@link placeholderCertificate}; `null` for the name-coloured encoding. */
|
|
690
|
+
readonly certificate: string | null;
|
|
691
|
+
/** Whether `horn` is the name-coloured encoding. */
|
|
692
|
+
readonly coloured: boolean;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* `(define-fun Reachable ((x!0 Int) …) Bool true)`: the certificate stand-in the
|
|
696
|
+
* golden certificate scripts are built around (a real certificate is solver output
|
|
697
|
+
* and never part of a golden).
|
|
698
|
+
*/
|
|
699
|
+
declare function placeholderCertificate(placeCount: number): string;
|
|
455
700
|
|
|
456
701
|
/**
|
|
457
|
-
*
|
|
702
|
+
* @module spacer-runner
|
|
703
|
+
*
|
|
704
|
+
* Runs Z3 Spacer on a HORN script through one `z3` process (VER-013) and
|
|
705
|
+
* classifies the reply in verdict terms.
|
|
706
|
+
*
|
|
707
|
+
* HORN/Spacer convention (shared with the Rust and Java verifiers and corroborated
|
|
708
|
+
* by the certificate check): with the query `(assert (not Error))`, z3 prints `sat`
|
|
709
|
+
* when the property is PROVEN (an inductive invariant excluding every violating
|
|
710
|
+
* state exists) and `unsat` when it is VIOLATED (no such invariant; the refutation
|
|
711
|
+
* proof carries the counterexample states).
|
|
458
712
|
*/
|
|
713
|
+
|
|
714
|
+
/** Result of a Spacer query. */
|
|
459
715
|
type QueryResult = QueryProven | QueryViolated | QueryUnknown;
|
|
460
|
-
/** Property proven
|
|
716
|
+
/** Property proven (z3 `sat`). */
|
|
461
717
|
interface QueryProven {
|
|
462
718
|
readonly type: 'proven';
|
|
719
|
+
/**
|
|
720
|
+
* The `(define-fun …)` block of the model, verbatim (the certificate the
|
|
721
|
+
* certificate checker re-validates), or `null` when no model printed.
|
|
722
|
+
*/
|
|
463
723
|
readonly invariantFormula: string | null;
|
|
464
|
-
readonly levelInvariants: readonly string[];
|
|
465
724
|
}
|
|
466
|
-
/**
|
|
725
|
+
/** Property violated (z3 `unsat`). */
|
|
467
726
|
interface QueryViolated {
|
|
468
727
|
readonly type: 'violated';
|
|
469
|
-
|
|
728
|
+
/** The raw solver reply; the refutation proof in it is decoded by the counterexample decoder. */
|
|
729
|
+
readonly answer: string;
|
|
470
730
|
}
|
|
471
|
-
/** Solver could not determine (timeout, resource limit). */
|
|
731
|
+
/** Solver could not determine (timeout, resource limit, transport failure). */
|
|
472
732
|
interface QueryUnknown {
|
|
473
733
|
readonly type: 'unknown';
|
|
474
734
|
readonly reason: string;
|
|
475
735
|
}
|
|
476
736
|
/**
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*/
|
|
480
|
-
interface SpacerContext {
|
|
481
|
-
/** The Z3 high-level context for building expressions. */
|
|
482
|
-
readonly ctx: ReturnType<Awaited<ReturnType<typeof init>>['Context']>;
|
|
483
|
-
/** The Z3 Fixedpoint solver instance (Spacer engine). Z3 types are complex; using any. */
|
|
484
|
-
readonly fp: any;
|
|
485
|
-
/** Queries whether the error state is reachable. */
|
|
486
|
-
query(errorExpr: Bool, reachableDecl?: FuncDecl): Promise<QueryResult>;
|
|
487
|
-
/** Releases Z3 resources. */
|
|
488
|
-
dispose(): void;
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Creates a Spacer runner with the given timeout.
|
|
492
|
-
*
|
|
493
|
-
* Uses Z3's Spacer engine (CHC solver based on IC3/PDR) to prove or
|
|
494
|
-
* disprove safety properties.
|
|
737
|
+
* Runs `smt2` with `fp.engine=spacer`. `phase` names the dump files (`horn` or
|
|
738
|
+
* `horn-coloured`).
|
|
495
739
|
*/
|
|
496
|
-
declare function
|
|
740
|
+
declare function runZ3Spacer(solver: Z3Solver, timeoutMs: number, smt2: string, phase: string): Promise<QueryResult>;
|
|
497
741
|
|
|
498
742
|
/**
|
|
499
743
|
* @module smt-encoder
|
|
500
744
|
*
|
|
501
|
-
* Encodes a flattened Petri net as Constrained Horn Clauses (CHC)
|
|
745
|
+
* Encodes a flattened Petri net as Constrained Horn Clauses (CHC) in SMT-LIB2 text
|
|
746
|
+
* for Z3's Spacer engine (VER-013).
|
|
502
747
|
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
748
|
+
* The net's state space is modeled as integer vectors (one variable per place = token
|
|
749
|
+
* count). Three rule types:
|
|
505
750
|
*
|
|
506
|
-
* 1. **Init**: `Reachable
|
|
507
|
-
* 2. **Transition**: `Reachable(M') :- Reachable(M) ∧ enabled(M,t) ∧ fire(M,M',t)
|
|
508
|
-
* one rule per flat transition
|
|
509
|
-
*
|
|
751
|
+
* 1. **Init**: `(assert (Reachable M0))` — the initial marking is reachable
|
|
752
|
+
* 2. **Transition**: `Reachable(M') :- Reachable(M) ∧ enabled(M,t) ∧ fire(M,M',t) ∧
|
|
753
|
+
* M' ≥ 0 ∧ invariants(M') ∧ env-bounds(M')` — one rule per flat transition, plus
|
|
754
|
+
* one env-injection rule per injected environment place (VER-006)
|
|
755
|
+
* 3. **Error**: `Error :- Reachable(M) ∧ violation(M)`; `(assert (not Error))`, so
|
|
756
|
+
* `sat` is PROVEN and `unsat` is VIOLATED
|
|
510
757
|
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
758
|
+
* The emitted script is byte-identical to the Rust reference (`smt_encoder.rs`) and
|
|
759
|
+
* the Java port for the same input: places in code-point order of their names, the
|
|
760
|
+
* property's places, sinks, env bounds and injections in place-index order,
|
|
761
|
+
* invariants in the order the verifier canonicalised.
|
|
762
|
+
*/
|
|
763
|
+
|
|
764
|
+
/** An encoded SMT-LIB2 script. */
|
|
765
|
+
interface SmtEncoding {
|
|
766
|
+
/** The script text. */
|
|
767
|
+
readonly smt2: string;
|
|
768
|
+
/** The number of flat places (the arity of `Reachable` in the flat encoding). */
|
|
769
|
+
readonly placeCount: number;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Encodes the net and property as a HORN script.
|
|
513
773
|
*
|
|
514
|
-
*
|
|
774
|
+
* @param produceProofs emit `:produce-proofs` and `(get-proof)` so an `unsat` reply
|
|
775
|
+
* carries the refutation the replay decodes
|
|
515
776
|
*/
|
|
777
|
+
declare function encode(flatNet: FlatNet, initialMarking: MarkingState, property: SmtProperty, invariants: readonly PInvariant[], sinkPlaces?: ReadonlySet<Place<any>>, produceProofs?: boolean): SmtEncoding;
|
|
778
|
+
/**
|
|
779
|
+
* The net's one-step relation `T(M, M')` as one plain SMT-LIB2 formula over the free
|
|
780
|
+
* variables `m0..` / `m0p..`: the disjunction of every flat transition firing and
|
|
781
|
+
* every env-injection step (VER-006). This is the UNSTRENGTHENED relation the
|
|
782
|
+
* certificate check validates against: it shares the condition emitters with the CHC
|
|
783
|
+
* path but omits the P-invariant conjuncts, so a certificate poisoned by a wrong
|
|
784
|
+
* invariant cannot re-certify itself.
|
|
785
|
+
*/
|
|
786
|
+
declare function encodeStepRelationSmt2(flatNet: FlatNet): string;
|
|
516
787
|
|
|
517
|
-
/** Z3 high-level context. Typed as `any` because z3-solver's TS types are incomplete. */
|
|
518
|
-
type Z3Context = any;
|
|
519
|
-
/** Z3 Fixedpoint solver instance. Typed as `any` because z3-solver's TS types are incomplete. */
|
|
520
|
-
type Z3Fixedpoint = any;
|
|
521
788
|
/**
|
|
522
|
-
*
|
|
789
|
+
* @module counterexample-decoder
|
|
790
|
+
*
|
|
791
|
+
* Decodes z3's refutation output into replayable counterexample material.
|
|
792
|
+
*
|
|
793
|
+
* There is exactly one decoder: {@link decodeStateSet}, which collects the ground
|
|
794
|
+
* `Reachable` facts of a `:produce-proofs` refutation into a SET. The ordered trace
|
|
795
|
+
* a caller sees is reconstructed from that set by the abstract replayer; the proof
|
|
796
|
+
* printer's traversal order is not a firing order and was never safe to read as one.
|
|
797
|
+
*
|
|
798
|
+
* Applications with non-ground arguments (rule bodies quantify `Reachable` over
|
|
799
|
+
* variables) or the wrong arity are skipped; a malformed proof simply yields a
|
|
800
|
+
* smaller (possibly empty) set, never a throw. Byte-for-byte mirror of the Rust
|
|
801
|
+
* `counterexample::decode_state_set`.
|
|
523
802
|
*/
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
803
|
+
|
|
804
|
+
/** Result of counterexample decoding. */
|
|
805
|
+
interface DecodedTrace {
|
|
806
|
+
/**
|
|
807
|
+
* The ground `Reachable` markings of the proof as an order-free set (text order
|
|
808
|
+
* preserved for display), what the abstract replayer chains into a firing order.
|
|
809
|
+
*/
|
|
810
|
+
readonly states: ReadonlySet<MarkingState>;
|
|
811
|
+
/** Why nothing was decoded; `null` when `states` is non-empty. */
|
|
812
|
+
readonly note: string | null;
|
|
527
813
|
}
|
|
814
|
+
/** Decodes the states of a z3 reply; a note says so when none were found. */
|
|
815
|
+
declare function decode(answer: string, flatNet: FlatNet): DecodedTrace;
|
|
528
816
|
/**
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
* CHC rules:
|
|
532
|
-
* - Reachable(M0) — initial state is reachable
|
|
533
|
-
* - Reachable(M') :- Reachable(M) AND enabled(M,t) AND fire(M,M',t) — transition rules
|
|
534
|
-
* - Error() :- Reachable(M) AND property_violation(M) — safety property
|
|
817
|
+
* Collects the ground `Reachable(...)` applications from a z3 refutation proof into
|
|
818
|
+
* a state set, in text order.
|
|
535
819
|
*/
|
|
536
|
-
declare function
|
|
820
|
+
declare function decodeStateSet(answer: string, flatNet: FlatNet): ReadonlySet<MarkingState>;
|
|
537
821
|
|
|
538
822
|
/**
|
|
539
|
-
*
|
|
823
|
+
* @module abstract-replayer
|
|
824
|
+
*
|
|
825
|
+
* Pure TS-side replayer for Spacer counterexamples over the ABSTRACT
|
|
826
|
+
* (untimed, value-blind) count-vector semantics — the exact semantics the CHC
|
|
827
|
+
* encoder emits and the Lean development verifies:
|
|
828
|
+
*
|
|
829
|
+
* - {@link enabledA} mirrors `lean/Libpetri/Basic.lean` `enabledA` and the
|
|
830
|
+
* encoder's `encodeEnabled` arm (smt-encoder.ts): every input place holds at
|
|
831
|
+
* least `pre[p]` tokens, every inhibited place is empty, every read place is
|
|
832
|
+
* non-empty.
|
|
833
|
+
* - {@link fireA} mirrors `Basic.lean` `fireA` and the encoder's `encodeFire`
|
|
834
|
+
* arm: a reset or consume-all (`All`/`AtLeast`) place jumps to `post[p]`;
|
|
835
|
+
* every other place moves by `M[p] - pre[p] + post[p]`.
|
|
836
|
+
* - {@link successors} mirrors one disjunct of `encodeStepRelation`: a firing
|
|
837
|
+
* is a successor only when its `M'` also respects `environmentBounds` (the
|
|
838
|
+
* `envBounds(M')` conjunct every transition disjunct carries), and one
|
|
839
|
+
* injection per modeled environment place whose guard admits it.
|
|
840
|
+
* - {@link injectA} mirrors `encodeInjectionFire`/`encodeInjectionGuard`
|
|
841
|
+
* (VER-006): one environment injection adds one token to the env place,
|
|
842
|
+
* gated by `M[p] < k` for `Bounded(k)` and unguarded for `AlwaysAvailable`.
|
|
843
|
+
* - {@link satisfiesBad} mirrors `encodePropertyViolation` — including the
|
|
844
|
+
* relax-env deadlock enablement and the declared-sink exemption — as a
|
|
845
|
+
* direct TS evaluator, so confirming a counterexample never needs a Z3 call.
|
|
846
|
+
*
|
|
847
|
+
* Because the abstraction over-approximates the concrete timed/valued net
|
|
848
|
+
* (VER-004), a decoded counterexample can be spurious. This module therefore
|
|
849
|
+
* only ever REPORTS an outcome ({@link ReplayOutcome}) — nothing here is
|
|
850
|
+
* allowed to certify by crashing, and only the `no-chain` outcome (a fully
|
|
851
|
+
* explored search that found no chain) is strong enough to withdraw a verdict.
|
|
540
852
|
*/
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
853
|
+
|
|
854
|
+
/** An abstract marking: token count per flat place index. */
|
|
855
|
+
type AbstractState = readonly number[];
|
|
856
|
+
/** One abstract step in a replayed chain. */
|
|
857
|
+
type ReplayStep = {
|
|
858
|
+
readonly kind: 'fire';
|
|
859
|
+
readonly transition: string;
|
|
860
|
+
} | {
|
|
861
|
+
readonly kind: 'inject';
|
|
862
|
+
readonly place: string;
|
|
863
|
+
};
|
|
864
|
+
/** Options for {@link replayCounterexample}. */
|
|
865
|
+
interface ReplayOptions {
|
|
866
|
+
/** Max abstract steps searched between decoded anchors (default 3). */
|
|
867
|
+
readonly segmentBudget?: number;
|
|
868
|
+
/**
|
|
869
|
+
* Max search nodes ADMITTED to the whole search (default 10_000).
|
|
870
|
+
*
|
|
871
|
+
* A node is admitted when it survives the segment budget and the domination
|
|
872
|
+
* check; dominated successors are never admitted and never counted. The root
|
|
873
|
+
* (`M₀`) counts as the first admitted node, and the search stops as soon as
|
|
874
|
+
* `nodeBudget` nodes have been admitted and another one is due — the same
|
|
875
|
+
* `>=`-before-admission rule the Rust and Java replayers apply, so the same
|
|
876
|
+
* nominal budget means the same effective search depth in all of them.
|
|
877
|
+
*/
|
|
878
|
+
readonly nodeBudget?: number;
|
|
544
879
|
}
|
|
545
880
|
/**
|
|
546
|
-
*
|
|
881
|
+
* Outcome of an abstract replay attempt.
|
|
882
|
+
*
|
|
883
|
+
* `confirmed` — a genuine abstract chain `M₀ → … → Bad` was found.
|
|
884
|
+
* `no-chain` — the search ran to completion without truncation and no chain
|
|
885
|
+
* exists: the counterexample is spurious or the decoder mis-read the
|
|
886
|
+
* derivation, and ONLY this outcome may withdraw a `violated` verdict.
|
|
887
|
+
* `exhausted` — the search was cut short (node or segment budget, or `M₀` was
|
|
888
|
+
* not among the decoded states), so nothing was proved either way.
|
|
889
|
+
*/
|
|
890
|
+
type ReplayOutcome = {
|
|
891
|
+
readonly kind: 'confirmed';
|
|
892
|
+
/** The replayed chain in FIRING order, `M₀ … M_bad` inclusive. */
|
|
893
|
+
readonly states: readonly AbstractState[];
|
|
894
|
+
/** One step per consecutive pair of {@link states}. */
|
|
895
|
+
readonly steps: readonly ReplayStep[];
|
|
896
|
+
readonly nodesExplored: number;
|
|
897
|
+
} | {
|
|
898
|
+
readonly kind: 'no-chain';
|
|
899
|
+
readonly nodesExplored: number;
|
|
900
|
+
} | {
|
|
901
|
+
readonly kind: 'exhausted';
|
|
902
|
+
readonly reason: string;
|
|
903
|
+
readonly nodesExplored: number;
|
|
904
|
+
};
|
|
905
|
+
/**
|
|
906
|
+
* Attempts to re-execute a decoded (order-free) counterexample state set in
|
|
907
|
+
* the abstract semantics.
|
|
547
908
|
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
909
|
+
* The decoder collects Spacer's `Reachable` applications in derivation
|
|
910
|
+
* TRAVERSAL order, which is not firing order; this search recovers a firing
|
|
911
|
+
* order or reports that none exists. It is a single global breadth-first
|
|
912
|
+
* search from `initial` over {@link successors}, where each node carries the
|
|
913
|
+
* number of steps taken since the last decoded state (`segment`, reset to 0
|
|
914
|
+
* whenever a decoded state is reached) and a node is expanded only while that
|
|
915
|
+
* counter is below `segmentBudget`. A state is re-entered only when reached
|
|
916
|
+
* with a strictly smaller segment counter (domination by `(state, segment)`),
|
|
917
|
+
* and the whole search shares one `nodeBudget` counting nodes ADMITTED to the
|
|
918
|
+
* search — non-dominated states only, the root included (see
|
|
919
|
+
* {@link ReplayOptions.nodeBudget}).
|
|
551
920
|
*/
|
|
552
|
-
declare function
|
|
921
|
+
declare function replayCounterexample(flatNet: FlatNet, initial: AbstractState, decodedStates: readonly AbstractState[], property: SmtProperty, sinkPlaces: ReadonlySet<Place<any>>, options?: ReplayOptions): ReplayOutcome;
|
|
553
922
|
|
|
554
923
|
/**
|
|
555
924
|
* Difference Bound Matrix (DBM) for Time Petri Net state class analysis.
|
|
@@ -740,4 +1109,4 @@ declare class TimePetriNetAnalyzerBuilder {
|
|
|
740
1109
|
build(): TimePetriNetAnalyzer;
|
|
741
1110
|
}
|
|
742
1111
|
|
|
743
|
-
export { type EnvironmentAnalysisMode as AnalysisEnvironmentMode, type BranchEdge, DBM, type DecodedTrace, type
|
|
1112
|
+
export { type AbstractState, type EnvironmentAnalysisMode as AnalysisEnvironmentMode, type BranchEdge, type CertificateCheckOutcome, type CertificateVc, DBM, DUMP_ENV, type DecodedTrace, type EncodedScripts, type 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, alwaysAvailable, alwaysAvailable as analysisAlwaysAvailable, bounded as analysisBounded, ignore as analysisIgnore, bounded, canonicalInvariantOrder, checkCertificate, computePInvariants, computePSemiflows, computeSCCs, decode, decodeStateSet, encode, encodeStepRelationSmt2, findMaximalTrapIn, findMinimalSiphons, findTerminalSCCs, flatNetIndexOf, flatNetPlaceCount, flatNetTransitionCount, flatTransition, flatten, formatZ3Version, ignore, isCoveredByInvariants, parseZ3Version, placeholderCertificate, replayCounterexample, resolveZ3, runZ3Spacer, runZ3Text, strengthenWithSemiflows, structuralCheck, vcScript, z3Available, z3SolverAt };
|