locus · logic lane · 2026-10-09
A logic of place and link that follows individual nodes through the reactions of a bigraphical reactive system, with every verdict checked by Lean's kernel.
PROVED here means checked by Lean's kernel using only the axioms propext and Quot.sound: no classical choice, no native_decide, no sorry.
A bigraph has nodes, each with a control (its kind: phil₀, fork, table) and a number of ports. The place graph is a forest: every node lies inside another node or directly in a region. The link graph puts each port on a link: a private edge or an outer name. A reaction rule R → R′ replaces an occurrence of the pattern R (the redex) by R′ (the reactum); holes in R (sites) match arbitrary content, the parameter, which the rule moves. In locus every rule is linear: each piece of parameter is used at most once. Nodes and edges are numbered, and agents that differ only by renumbering are support equivalent, a ≏ b.
Condensed from a survey of about ninety verified references (docs/research/spatial-behavioural-logics.md).
BiLog for bigraphs, the ambient logic and the spatial logic of the π-calculus can say "this agent is two parallel parts, one satisfying A, the other B". They describe the shape of a state precisely. Their dynamics is at most a next-step modality, and none follows an individual node through time.
They are also, provably, not logics of behaviour. A logic that can say "exactly two parallel parts" can count and inspect components, so two agents that behave alike but are built differently satisfy different formulas: the logic's notion of "the same" is same structure (Sangiorgi 2001; Hirschkoff, Lozes and Sangiorgi 2008). For bigraphs that is ≏, not bisimilarity. This is why we do not ask the new logic to characterise an equivalence.
For CCS with localities (Boudol, Castellani, Hennessy and Kiehn 1993), modalities record the location of an action, and logical equivalence is location equivalence. Locations there are labels on actions, not a space that evolves.
Temporal logics for graph rewriting with variables for nodes and edges, carried along each step by a counterpart map (Gadducci, Laretto and Trotta 2023/2026, mechanised in Agda; earlier Distefano, Rensink and Katoen 2002). When the counterpart maps are partial functions, the usual laws of fixpoint logic hold. This is the template we port.
No published bigraph logic quantifies over the same node or link across reactions. Tracking maps exist only inside tools.
A tracked rule is a rule with a tracking list τ of pairs (u, w): redex item u becomes reactum item w. It must be well formed: no item twice on either side, nodes to nodes, edges to edges.
For a reaction a → a′ through a decomposition a = D ∘ (R with parameter d), the tracking map f sends each node or edge of a to its counterpart in a′, or nowhere:
| an item of a in … | goes to … |
|---|---|
| the context D | itself (renumbered as a′ is) |
| the redex R | its τ-partner in R′, if τ names one; otherwise it is deleted |
| the parameter d | its copy in the reactum's site; deleted if that piece is discarded |
We write a →tf a′ for a reaction with tag t and tracking map f. Outer names are fixed by the interface and are not tracked.
structure TrRule (α Ctrl : Type) where rule : RuleD Ctrl -- the simulator's rule, unchanged tag : Option α -- action tag (none = internal) τ : List (Nat × Nat) -- redex item u becomes reactum item w
Proved (trackDefined). Every library reaction has a tracking map, and with well-formed τ every tracking map is one-to-one and sends nodes to nodes and edges to edges. Linearity is what makes this true: a copied parameter would have two counterparts.
Variables x, y range over nodes, ℓ over links, X over sets of states.
| atom | reads | graph |
|---|---|---|
| ctrl(x) = K | node x has control K | — |
| prnt(x, y) | x lies directly inside y | place |
| root(x) = r | x lies directly in region r | place |
| anc(x, y) | x lies somewhere inside y | place |
| port(x, i) = ℓ | port i of x is on link ℓ | link |
| ℓ = name y | link ℓ is the outer name y | link |
| x = y | the same node or link | — |
Meaning. A formula holds at a state with a valuation of its variables.
| ρ, a, σ, .dia t φ => ∃ a' f, TrReact S trs t a a' f ∧ PSat trs ρ a' (σ.tr f) φ
| ρ, a, σ, .box t φ => ∀ a' f, TrReact S trs t a a' f → PSat trs ρ a' (σ.tr f) φ
-- σ.tr f : move every variable along the tracking map f
Proved (psatInv). Renumbering a state, with its variables, changes no truth value. The logic is about bigraphs up to ≏.
Asking the kernel to search a state space does not finish, so locus checks models by certificate:
Proved (trackSoundAll). If the checker accepts and the evaluator says φ is true at a listed state, φ is true there in the library's semantics, for every formula. The rules must satisfy three conditions, each checked by decide:
cocc_G1);The core is tracked matcher completeness (trCOcc): every tracked library reaction is found by the matcher, with the same tracking map, up to renumbering. Without it a certificate could leave out a step and make a "for every step" formula look true.
False verdicts, by duality (psat_dual, trackFalse). Swapping ∧/∨, ∃/∀, ⟨⟩/[], μ/ν and p/¬p gives the dual of a formula; we prove, constructively, that a true dual makes the formula false. "φ is false" is certified by checking its dual true.
The simulator's matcher and the existing proofs about it are unchanged. The tracked proof reuses them by import and does not replace them: it has extra side conditions, so it does not subsume cocc_G1.
Three philosophers, three forks, the fair protocol: pick up the left fork, then the right, eat, put both down; a hungry philosopher whose right fork is taken puts its left fork back. Philosophers and forks are tracked; their states are not. Writing onTable(x) := ∃y. ctrl(y)=table ∧ prnt(x,y) and heldByj(x) := ∃q. ctrl(q)=philj ∧ prnt(x,q):
| property | formula | verdict |
|---|---|---|
| returns: always, any fork phil₀ holds can later be on the table again, the same fork | AG ∀p∀x. (phil₀(p) ∧ fork(x) ∧ prnt(x,p)) → μX. onTable(x) ∨ ⟨→⟩X | TRUE |
| returnsSome: some fork phil₀ holds can later be on the table again | EF ∃x. heldBy₀(x) ∧ μX. onTable(x) ∨ ⟨→⟩X | TRUE |
| handOver: some fork phil₀ holds is later held by phil₂ | EF ∃x. heldBy₀(x) ∧ μX. heldBy₂(x) ∨ ⟨→⟩X | TRUE |
| bypass: … and gets there without passing the table | EF ∃x. heldBy₀(x) ∧ μX. heldBy₂(x) ∨ (¬onTable(x) ∧ ⟨→⟩X) | FALSE |
| pickSame: the fork phil₁ picks up from the table is that same fork | ∀x. (fork(x) ∧ onTable(x)) → [pickLeft₁](onTable(x) ∨ heldBy₁(x)) | TRUE |
| forksPersist: no step deletes a fork | AG ∀x. fork(x) → [→] x = x | TRUE |
All six are theorems about the library meaning at the initial state, proved from one 14-state certificate in about 64 seconds of kernel time. With the same rules but no tracking, "returns", "pickSame" and "forksPersist" come out false: after one step the variable has no counterpart, so "the same fork later" cannot be said at all. The gate has been watched failing: corrupting one entry of one tracking list makes the certificate check fail.
The ABP sends messages over two lossy one-place channels and retransmits until acknowledged. Retransmission shapes the model: a copy of a message is a new node, since linear rules cannot duplicate one. So a message's identity is a link.
held(ℓ): the sender holds a message on link ℓ; out(ℓ): the user holds one. The model has 224 states and 39 linear rules.
Compiled code explores the states and computes every verdict; the kernel only checks certificates, with four checkers each proved sound:
| certificate | proves |
|---|---|
| the closed set of reachable states, property checked state by state | AG ψ |
| a path to a counterexample state | ¬ AG ψ |
| witness paths: a run per obligation, carrying ℓ through the tracking to a goal | μX. φ ∨ ⟨t⟩X |
| an invariant table of (state, value of ℓ) pairs, closed under tracked steps | νY. φ ∧ [t]Y |
| property | formula | verdict |
|---|---|---|
| sameMessage: the delivered message is the one the sender held when it was delivered | AG ∀ℓ. [deliver](out(ℓ) → held(ℓ)) | TRUE |
| heldNow (control): … the one the sender holds now | AG ∀ℓ. out(ℓ) → held(ℓ) | FALSE |
| canDeliver: every accepted message can still be delivered | AG [accept] ∀ℓ. held(ℓ) → μX. ⟨deliver⟩out(ℓ) ∨ ⟨τ⟩X | TRUE |
| noDuplicate: no message is delivered twice | AG ∀ℓ. [deliver](out(ℓ) → νY. [deliver]¬out(ℓ) ∧ [→]Y) | TRUE |
| noStray: every copy in the channel is of the current message or stale | invariant | TRUE |
| copyGone (control): no copy remains after delivery | invariant | FALSE |
Twelve verdicts in all, eight TRUE and four FALSE (the others: heldStable, dupEntry, dupStep and canDeliverC TRUE; noStrayStrict and canDeliverHeldC FALSE), checked in about six minutes over separately built modules of under 4 GB each, every checker watched rejecting a corrupted certificate. With tracking ignored the model is weakly bisimilar over rule tags to the one-place buffer (accept and deliver visible; messages carry no data): proved, abpTrack_wbisim_buff, built on demand.
A finding: with nothing of any redex tracked, only heldStable changes verdict. Message links mostly survive a step in the context, which is tracked automatically; only the rules that delete a copy need their own tracking.
| statement | Lean |
|---|---|
| tracking is defined for every reaction; one-to-one, sort-preserving | trackDefined |
| invariance under renumbering | psatInv |
| a matcher step is a tracked library reaction with the computed map | trReact_occ |
| soundness for existential formulas, no side conditions | trackSound |
| tracked matcher completeness (G1, no idle redex edge, well-formed τ) | trCOcc |
| soundness for every formula (same conditions) | trackSoundAll |
| duality; certified false verdicts | psat_dual, trackFalse |
| the six dining verdicts | returns_holds … bypass_false |
| four certificate checkers (closed set, path, witness paths, invariant table) | agCheck_sound, agFalse, muWalk_sound, nuCheck_sound |
| twelve ABP verdicts | sameMessage_holds … copyGone_false |
About 4,200 lines of Lean in Logic/Track*.lean; the dining certificate is Logic/Examples/M1Prime/DiningTrackCert.lean. Open: completeness of the checker itself (not needed, since false verdicts go through duality).
| piece | status |
|---|---|
| counterpart maps; variables carried along steps; laws for partial maps | KNOWN: Gadducci–Laretto–Trotta (Agda) |
| dead-variable reading | KNOWN: Distefano–Rensink–Katoen 2002 |
| tracked rules | KNOWN: Milner's tracking map is on supports, that is nodes and edges; ours runs forwards and must be injective |
| linear rules give one-to-one tracking | FOLKLORE |
| a place-and-link logic over tracked bigraph reactions | APPARENTLY NEW |
| its certified checking against a mechanised bigraph semantics | APPARENTLY NEW |
| spatial logics characterise structure, not behaviour | KNOWN: Sangiorgi; Hirschkoff–Lozes–Sangiorgi |
APPARENTLY NEW means searched for and not found. It is not a claim of originality; that decision is Matthew's.
c1.)Source of record: docs/logic-tracked.md in the locus repository, with statuses in docs/logic-statements.md. Reproduce with lake build Logic and lake env lean Logic/Examples/M1Prime/DiningTrackCert.lean.