<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title><![CDATA[ARIA SgAI Notebook]]></title>
    <link>https://aria-sgai-notebook.inkandswitch.com/</link>
    <description><![CDATA[Notes, essays, and working documents from the ARIA Safeguarded AI programme.]]></description>
    <lastBuildDate>Wed, 09 Sep 2026 20:24:22 GMT</lastBuildDate>
    <atom:link href="https://aria-sgai-notebook.inkandswitch.com/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title><![CDATA[SDCPNs: A Common Language for Complex Systems]]></title>
      <link>https://aria-sgai-notebook.inkandswitch.com/sdcpns/</link>
      <guid isPermaLink="true">https://aria-sgai-notebook.inkandswitch.com/sdcpns/</guid>
      <pubDate>Thu, 03 Sep 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[Formal guarantees of AI safety begin as a modelling problem: to reason rigorously about whether an AI-controlled system is safe, you first need a mathematical representation of that system. This post builds a Stochastic Dynamic Coloured Petri Net up from an ordinary Petri net, one feature at a time, and then uses three cyber-physical examples — an industrial gas supply chain, a truck fleet, and a semiconductor fab — to show the range of systems the resulting formalism can represent.]]></description>
      <content:encoded><![CDATA[<p>We want to provide formal guarantees of AI safety across a wide range of domains and applications. That creates a modelling problem before it creates an AI problem: if we want to reason rigorously about whether an AI-controlled system is safe, we first need a mathematical representation of that system and the potential consequences of action within it.</p>
<p>There are many established mathematical languages for describing the world. Differential equations describe quantities that evolve continuously over time, such as temperature or motion. State machines and automata describe systems that move between discrete modes. Markov decision processes capture decision-making under uncertainty. Petri nets represent concurrency and competition for shared resources. Each of these formalisms help model different sorts of behaviours that we find in the real world.</p>
<p>Hybrid-system formalisms are designed to bring multiple of these lenses together. We are investing in <em>Stochastic Dynamic Coloured Petri Nets</em> (SDCPNs), a Petri net variant that provides a way to represent continuous and discrete behaviour, multiple interacting entities, structured state, data-dependent decisions, concurrency and randomness within a single framework.</p>
<p>That combination is particularly useful for <em>cyber-physical</em> systems: where a physical process is monitored and even controlled by digital elements. A physical change may enable or block a decision; a decision in cyberspace can alter a physical trajectory; and failures often emerge not from either side independently, but from their interaction.</p>
<p>In this post we’ll build up an SDCPN from an ordinary Petri net, explain what each extension adds, and then use several cyber-physical examples to show the range of systems the resulting formalism can represent.</p>
<h2 id="what-is-an-sdcpn">What is an SDCPN?</h2>
<p>A <a href="https://petrinaut.org/">Petri net</a> is a directed bipartite graph where <strong>places</strong> (circles) and <strong>transitions</strong> (rectangles) are connected by <strong>arcs</strong> (edges/arrows), in totality representing a process. Places can represent conditions or resource buckets and are <em>marked</em> by <strong>tokens</strong>, with the markings recording the state of the process at any given moment. Transitions are enabled by the presence of tokens in their input places, and when they <em>fire</em> they consume tokens from their input places and produce tokens in their output places, thereby changing the state of the process.</p>
<p>For example, a very simple Petri net modelling a pedestrian traffic light might look like this:</p>
<figure class="border autoplay">
<video controls playsinline preload autoplay loop muted aria-label="An animated Petri net of a pedestrian traffic light: a token moves from a Red place through a Switch to Green transition into a Green place, then back through Switch to Red, cycling continuously." src="https://aria-sgai-notebook.inkandswitch.com/sdcpns/traffic-light.mp4"></video>
<figcaption>
<p>A pedestrian traffic light as a plain Petri net, drawn in Petrinaut. The token moves from <em>Red</em> to <em>Green</em> as each transition fires, and back again.</p>
</figcaption>
</figure>
<p>The above Petri net includes two places, Red and Green, connected by two transitions. The net starts with one token in Red, when “Switch to Green” fires, it moves to Green; when “Switch to Red” fires, it moves back to Red. This illustrates the logic of switching between green and red on a traffic light. In practice the firings would be near-instantaneous.</p>
<p>A plain Petri net records what can happen and in what order, but without modelling durations, likelihoods, or what distinguishes one token from another, and it cannot answer “how often” or “how likely” questions about a given scenario.</p>
<p>An SDCPN extends a plain Petri net with 4 features:</p>
<ul>
<li>
<p><strong>Colour</strong>. Each place is associated with a <em>colour set</em> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi></mrow><annotation encoding="application/x-tex">C</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0715em;">C</span></span></span></span>, which defines the type and structure of data that tokens in that place may carry. A token residing in the place has a particular <em>colouring</em> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>∈</mo><mi>C</mi></mrow><annotation encoding="application/x-tex">x \in C</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.5782em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0715em;">C</span></span></span></span>: that is, a specific assignment of values to the attributes defined by the place’s colour set. Colouring therefore makes otherwise identical tokens distinguishable. For example, if a place represents stored food products, its colour set might specify the tuple <em>(product ID, production date, expiry date, …)</em>, while an individual token could have colouring <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>=</mo><mo stretchy="false">(</mo><mtext>P123</mtext><mo separator="true">,</mo><mn>01</mn><mi mathvariant="normal">/</mi><mn>06</mn><mi mathvariant="normal">/</mi><mn>2026</mn><mo separator="true">,</mo><mn>01</mn><mi mathvariant="normal">/</mi><mn>09</mn><mi mathvariant="normal">/</mi><mn>2026</mn><mo separator="true">,</mo><mo>…</mo><mtext> </mtext><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">x = (\text{P123}, 01/06/2026, 01/09/2026, \dots)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord text"><span class="mord">P123</span></span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">01/06/2026</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">01/09/2026</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="minner">…</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mclose">)</span></span></span></span>. Token values can be used to influence net behaviour, for example the introduction of a transition guard function <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">G</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span>, which evaluates the colouring <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> of the tokens available to a transition. The transition is enabled when <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">G(x)=1</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">G</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:0.6444em;"></span><span class="mord">1</span></span></span></span> and disabled when <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">G(x)=0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">G</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span>. For instance, a transition representing “ship to customer” could have a guard <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">G</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> = 1 if the product’s expiry_date &gt; today + 365 days, and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">G(x)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">G</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span></span></span></span> = 0 otherwise, so that only products that have a sufficient shelf life are eligible for shipment.</p>
</li>
<li>
<p><strong>Stochastic firing.</strong> Rather than firing immediately once enabled, each enabled transition <em>i</em> independently draws a random firing delay from a probability distribution whose parameters can depend on the token’s colouring <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span>. In the common exponential case, <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><mtext>fire in </mtext><mi>d</mi><mi>t</mi><mo stretchy="false">)</mo><mo>≈</mo><msub><mi>λ</mi><mi>i</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mtext> </mtext><mi>d</mi><mi>t</mi></mrow><annotation encoding="application/x-tex">P(\text{fire in } dt) \approx \lambda_i(x)\,dt</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">P</span><span class="mopen">(</span><span class="mord text"><span class="mord">fire in </span></span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord"><span class="mord mathnormal">λ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="katex-sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span></span></span></span>, where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>λ</mi><mi>i</mi></msub></mrow><annotation encoding="application/x-tex">\lambda_i</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.8444em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal">λ</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3117em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="katex-sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">i</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span></span></span></span> is the firing rate of transition <em>i</em>. When multiple transitions are enabled, the one with the shortest sampled delay fires first. A higher <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6944em;"></span><span class="mord mathnormal">λ</span></span></span></span> means a shorter expected delay, so faster processes tend to fire before slower ones, but a slower transition can still occasionally fire first. For example, the chance of a delivery van breaking down can depend on colour dimensions which record the van’s age and the length of time since its last maintenance.</p>
</li>
<li>
<p><strong>Deterministic dynamics (ordinary differential equations - ODEs).</strong> While a token sits in a place, its colouring <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.4306em;"></span><span class="mord mathnormal">x</span></span></span></span> (e.g. fluid level, machine’s wear) evolves continuously through</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>d</mi><mi>x</mi><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">)</mo><mtext> </mtext><mi>d</mi><mi>t</mi></mrow><annotation encoding="application/x-tex">dx = f(x, p)\,dt</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">p</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span></span></span></span></span>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f(x, p)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">p</span><span class="mclose">)</span></span></span></span> is the deterministic drift term which dictates the trend of the colouring evolution and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow><annotation encoding="application/x-tex">p</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.625em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span></span></span></span> is the set of fixed constants such as flow rates or degradation coefficients. This evolution replaces the assumption of static token colouring: a token’s data changes continuously while it remains in a place, according to differential equations that can represent processes such as material consumption, fluid-level changes, or progressive equipment degradation. Whether a token enables a transition or at what rate the enabled transition will fire can therefore evolve over time depending on the evolution of token data. We can use this to model the freshness of food decreasing over time at a rate of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>x</mi><mo>=</mo><mo>−</mo><mi>k</mi><msup><mi>e</mi><mrow><mo>−</mo><mi>T</mi><mi mathvariant="normal">/</mi><mn>20</mn></mrow></msup><mtext> </mtext><mi>d</mi><mi>t</mi></mrow><annotation encoding="application/x-tex">dx = -k e^{-T/20}\,dt</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:0.9713em;vertical-align:-0.0833em;"></span><span class="mord">−</span><span class="mord mathnormal" style="margin-right:0.0315em;">k</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.888em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="katex-sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mathnormal mtight" style="margin-right:0.1389em;">T</span><span class="mord mtight">/20</span></span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span></span></span></span>, where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow><annotation encoding="application/x-tex">k</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6944em;"></span><span class="mord mathnormal" style="margin-right:0.0315em;">k</span></span></span></span> is a base decay rate and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow><annotation encoding="application/x-tex">T</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">T</span></span></span></span> is the storage temperature, so warmer conditions accelerate the loss of freshness and the product becomes unsellable when its freshness crosses a threshold.</p>
</li>
<li>
<p><strong>Stochastic dynamics (stochastic differential equations - SDEs).</strong> The differential equations can include random noise, so continuous evolution can be perturbed by random noise. This is represented as a stochastic differential equation (SDE):</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>d</mi><mi>x</mi><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">)</mo><mtext> </mtext><mi>d</mi><mi>t</mi><mo>+</mo><mi>σ</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">)</mo><mtext> </mtext><mi>d</mi><mi>B</mi></mrow><annotation encoding="application/x-tex">dx = f(x, p)\,dt + \sigma(x, p)\,dB</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6944em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">x</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">p</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2222em;"></span><span class="mbin">+</span><span class="mspace" style="margin-right:0.2222em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">σ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">p</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.0502em;">B</span></span></span></span></span>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow><annotation encoding="application/x-tex">B</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6833em;"></span><span class="mord mathnormal" style="margin-right:0.0502em;">B</span></span></span></span> denotes a stochastic process such as the Wiener process through which randomness enters and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>σ</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\sigma(x, p)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.0359em;">σ</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">p</span><span class="mclose">)</span></span></span></span> is the diffusion term acting as the scaling factor of randomness. The first term <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">f(x, p)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1076em;">f</span><span class="mopen">(</span><span class="mord mathnormal">x</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">p</span><span class="mclose">)</span></span></span></span> is the same drift term appearing in deterministic dynamics. Examples of stochastic dynamics are demand rates or ambient temperature drifting randomly.</p>
</li>
</ul>
<p>The four features together allow SDCPNs to represent all aspects of a cyber-physical system in the same state and clock: its physical process (a tank emptying, a machine wearing), control logic (when to dispatch, when to service), and the randomness that affects both (a breakdown, a demand spike).</p>
<h3 id="the-origin-of-sdcpns">The origin of SDCPNs</h3>
<p>While ordinary Petri nets date back to the 1960s, SDCPNs were developed by <a href="https://link.springer.com/chapter/10.1007/11587392_10">Mariken Everdij and Henk Blom</a> at the National Aerospace Laboratory (NLR) in the Netherlands, in the early 2000s. The problem they were trying to solve was quantifying the probability of a mid-air collision, an event too rare to observe in testing, and requires modelling many aircraft moving continuously, controlled by different humans and automated systems. Everdij and Blom extended the Dynamically Coloured Petri Net (DCPN) by adding Brownian motion to the equations governing how token data evolves between firings, and defined the result as a formal 12-component specification (<a href="https://old.sf.bg.ac.rs/downloads/katedre/apatc/ICRAT2010.pdf">P, T, A, N, S, C, V, W, G, D, F, I</a>).</p>
<p>The formalism was designed to support a safety case, expressed in probabilistic terms: a defensible and quantitative statement about how often something bad happens in a system too complex to test exhaustively. With a view to enabling the same kind of guarantees to be made regarding the behaviour of frontier AI models, the ARIA <a href="https://aria.org.uk/opportunity-spaces/trust-everything-everywhere/safeguarded-ai">Safeguarded AI</a> (SgAI) programme is also building on SDCPNs. Our goal is to enable the development of world models expressive enough to represent both complex physical systems and the AI that control them, with a safety specification over both. The air traffic problem, with continuous dynamics, multiple agents, human and automated controllers, rare catastrophic events is exactly the class of system SgAI is building for. The only difference is the controller: the original use case modelled human operators and procedural automation, while SgAI focuses on neural networks.</p>
<h2 id="modelling-the-real-world-with-sdcpns">Modelling the real world with SDCPNs</h2>
<p>The three sections that follow model real systems as SDCPNs. The first, an industrial gas supply chain, is built progressively: one feature of the formalism is added at each step until the model is a full SDCPN. The other two, truck fleet maintenance and semiconductor fabrication, are described as full SDCPNs from the start, to illustrate the range of domains the formalism reaches.</p>
<h2 id="industrial-gas-supply-chain">Industrial gas supply chain</h2>
<p>Taking an industrial gases supply chain as an example use case: a gas supplier delivers liquid gases (e.g. nitrogen, oxygen) to customer sites by road tankers. The supply chain operates in a standard practice where the supplier owns the liquid in each customer’s tank, reads the level by telemetry, and decides when to send a refill. The customer draws product as needed and only pays for what they consume, but does not place orders.</p>
<p>Each tank empties from two sources: the customer’s consumption and heat leaking through the walls, which boils liquid gas off continuously. A delivery that arrives before enough space exists risks overfilling. The supplier must keep every tank within a safe range:</p>
<ul>
<li>
<p><strong>Too low</strong>: the customer’s production line stops (stockout).</p>
</li>
<li>
<p><strong>Too full</strong>: boil-off gas has nowhere to go, pressure rises, and the relief valve vents the gases into the atmosphere, creating wastage and potential safety concerns.</p>
</li>
</ul>
<p>The reorder point, load size, consumption rate, boil-off rate, and delivery lead time are all coupled. A tanker dispatched to one site is unavailable to other customer sites until it returns.</p>
<h3 id="plain-petri-net">Plain Petri net</h3>
<figure class="petrinaut wide">
<iframe class="petrinaut-frame" src="https://demo.petrinaut.org/embed/examples/gases-1-pn" title="Gases 1 — one customer, ordering on a level trigger. Open in Petrinaut" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen></iframe>
<figcaption>
<p>Gases 1 — one customer, ordering on a level trigger. <a href="https://demo.petrinaut.org/examples/gases-1-pn">Open in Petrinaut</a></p>
</figcaption>
</figure>
<p>First we model the system using a plain Petri net with only places, transitions, tokens (without colours) and different arcs and arc weights.</p>
<p>The net consists of just 1 customer site to illustrate the core concepts. To start, there is a tank of nitrogen on site holding 42 out of 54 units of liquid gas; one tanker at the depot and up to 2 loads may be on order at once. The customer’s production line is running.</p>
<p>As the customer consumes the nitrogen and some boils off, the level drops. When the level drops to 15 (representing the telemetry-based sensor in the tank), an order is placed. A tanker dispatches, arrives and delivers 12 units (only if 12 units of space exist in the tank). The permit and tanker return on delivery and cycle repeats.</p>
<p>Stockout happens if the tank hits zero and the production line stops. It restarts when at least 1 unit arrives.</p>
<p>If the tank is completely full, a relief valve opens and reduces the level of gas (by 1 unit). Under this level trigger order policy, venting is unreachable since the maths of the reorder point and load size prevent it (gas only refills by 12 units when at or below 15 units). In later timed-extensions of the model, we introduce pressure-driven venting since in practice, venting is required when pressure gradually builds as the liquid warms.</p>
<figure class="petrinaut wide">
<iframe class="petrinaut-frame" src="https://demo.petrinaut.org/embed/examples/gases-1-pn-consumption-trigger" title="Gases 1 — the consumption-trigger variant. Open in Petrinaut" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen></iframe>
<figcaption>
<p>Gases 1 — the consumption-trigger variant. <a href="https://demo.petrinaut.org/examples/gases-1-pn-consumption-trigger">Open in Petrinaut</a></p>
</figcaption>
</figure>
<p>The above shows the net for a variant of the order policy based on consumption-trigger. Instead of reordering when the level drops below a threshold, the system reorders after every 8 units are drawn by the customer without accounting for any evaporation. This results in a failure mode whereby, If enough nitrogen boils off, the tank empties without the consumption counter ever reaching 8. The system reaches a deadlock: the tank is at zero, fewer than 8 units have been drawn since the last order, and nothing in the model can change the state in the system so the production line stops and never restarts.</p>
<p>Without time accounted for in the model, the net picks any enabled transition to fire without any rules on ordering. There can be a scenario where the transition for consuming nitrogen is fired repeatedly and empties the contents without dispatching the tanker for refill. Adding durations fixes this so events happen according to rates rather than random choice, which we explore in the next progression where stochastic firing rates are added.</p>
<p>This basic Petri net is useful for checking the logic of the system, such as finding deadlocks (consumption-trigger variant), checking conservation laws (liquid + ullage = 54 always) etc. Without the additional features, it falls short on answering the more interesting timing questions like whether the tank runs dry before the tanker arrives, or what load size and reorder point minimise stockouts.</p>
<h3 id="add-time">Add time</h3>
<figure class="petrinaut wide">
<iframe class="petrinaut-frame" src="https://demo.petrinaut.org/embed/examples/gases-2-spn" title="Gases 2 — a stochastic net, with one tanker shared between two sites. Open in Petrinaut" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen></iframe>
<figcaption>
<p>Gases 2 — a stochastic net, with one tanker shared between two sites. <a href="https://demo.petrinaut.org/examples/gases-2-spn">Open in Petrinaut</a></p>
</figcaption>
</figure>
<p>The above shows a Stochastic Petri net (SPN), based on the same structure as the basic Petri net with the addition of rates on each transition and a second site (SlowNitrogen) to represent a low-consumption customer.</p>
<p>Each enabled transition now independently draws a random delay time from a general family of distributions. One prominent distribution in this family is the exponential distribution, where each transition has a random delay <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi><mo>∈</mo><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mi mathvariant="normal">∞</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">t \in (0, \infty)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6542em;vertical-align:-0.0391em;"></span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∈</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mopen">(</span><span class="mord">0</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord">∞</span><span class="mclose">)</span></span></span></span> drawn from:</p>
<span class="katex-display"><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><mi>t</mi><mo>∣</mo><mi>λ</mi><mo stretchy="false">)</mo><mo>=</mo><mi>λ</mi><msup><mi>e</mi><mrow><mo>−</mo><mi>λ</mi><mi>t</mi></mrow></msup></mrow><annotation encoding="application/x-tex">P(t \mid \lambda) = \lambda e^{-\lambda t}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal" style="margin-right:0.1389em;">P</span><span class="mopen">(</span><span class="mord mathnormal">t</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">∣</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">λ</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:0.8991em;"></span><span class="mord mathnormal">λ</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8991em;"><span style="top:-3.113em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="katex-sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mathnormal mtight">λ</span><span class="mord mathnormal mtight">t</span></span></span></span></span></span></span></span></span></span></span></span></span>
<p>where <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6944em;"></span><span class="mord mathnormal">λ</span></span></span></span> is the average delay per hour (firing rate) for that transition. So whichever transition is delayed the least fires first. For example, when the customer’s (SteadyNitrogen) tank is half full, three transitions are enabled at once: the customer drawing product (rate 0.80/h), boil-off (0.16/h), and the tanker arriving (rate 1/6 ≈ 0.17/h). The runtime engine rolls a random delay time for each: 0.45 hours for product draw, 3.1 hours for boil-off, and 2.8 hours for the arrival. The nitrogen draw transition fires first so one unit leaves the tank, the clock advances 0.45 hours, and all three roll again from scratch.</p>
<p>A limitation of modelling journey time as a stochastic rate on the arrival transition (1/6 or 1/9 per hour, giving mean journeys of 6 and 9 hours) is that the exponential distribution allows a delivery to arrive almost immediately or take far longer than its mean. Realistic journey times need a minimum and a bounded spread, which requires clocks counted down by dynamics.</p>
<p>With the addition of a second customer, the one supplier tanker serves both sites with a new “returning” state to represent the time the tanker spends driving home after each delivery, introducing a risk of stockout if one customer is left waiting whilst the tanker delivers for another. For example, if the SteadyNitrogen tank crosses its reorder threshold (16 units) while the tanker is mid-journey to the SlowNitrogen site, it must wait for the delivery to complete (up to 9 hours remaining), the return trip (~4 hours) plus its own journey (~6 hours) before receiving the product. At a combined drain of ~0.96 units/hour, 16 units of buffer lasts roughly 17 hours, so on some runs can result in a stockout for the high consumption customer (SteadyNitrogen).</p>
<p>This stochastic net introduces time and contention with transitions competing to fire next and additional customers that can lock out delivery for each other. It answers questions on stockout frequency, the cost of sharing a tanker and the effect of slower routes. It falls short on the realism of timing (a 6-hour journey is exponentially distributed, so it sometimes arrives in minutes and sometimes takes a day), distinguishing between different products, and continuous processes like boil-off are still modelled as discrete random events rather than a steady flow.</p>
<h3 id="add-data-types-colours">Add data types (colours)</h3>
<figure class="petrinaut wide">
<iframe class="petrinaut-frame" src="https://demo.petrinaut.org/embed/examples/gases-3-cpn" title="Gases 3 — colours added, a mixed fleet across three customers. Open in Petrinaut" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen></iframe>
<figcaption>
<p>Gases 3 — colours added, a mixed fleet across three customers. <a href="https://demo.petrinaut.org/examples/gases-3-cpn">Open in Petrinaut</a></p>
</figcaption>
</figure>
<p>To distinguish which product a tanker is holding, we can add colours so each tanker token now has a data field identifying its gas type. In this example, the timing mechanism stays stochastic but the network grows to three customers and three tankers.</p>
<p>The third customer requires a different type of gas, oxygen with a draw rate of 0.60/h and average delivery journey time of 12 hours. The depot now holds 3 tankers (2 nitrogen, 1 oxygen) and a transition guard checks the correct product is dispatched. If a nitrogen order is placed and only the oxygen tanker is idle, the order waits. The cycle for drawing, boiling-off and ordering works as before.</p>
<p>With product identity added to the fleet, the SPN with colours can answer questions on whether fleet composition matters more than fleet count and by how much. For example, the same three tankers re-specified from one to two oxygen tankers can eliminate the oxygen stockout completely if they are the most critical customer from a business perspective. The colour features also allows for modelling of tanker spot hires. For example if 3 or more orders are waiting and no tanker is idle, a hire transition fires: its kernel writes a new tanker token into the depot with a rental flag, and a release transition destroys it when the demand clears. (This is included in the next progression where the colours are evolving (dynamics).)</p>
<p>This model still has the same timing limitations as the SPN: exponential journey times and discrete boil-off events.</p>
<h3 id="add-continuous-evolution">Add continuous evolution</h3>
<figure class="petrinaut wide">
<iframe class="petrinaut-frame" src="https://demo.petrinaut.org/embed/examples/gases-4-dcpn" title="Gases 4 — the full SDCPN: colour with continuous dynamics. Open in Petrinaut" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen></iframe>
<figcaption>
<p>Gases 4 — the full SDCPN: colour with continuous dynamics. <a href="https://demo.petrinaut.org/examples/gases-4-dcpn">Open in Petrinaut</a></p>
</figcaption>
</figure>
<p>This model adds dynamic colours and replaces the stack of unit tokens with a single token governed by differential equations, to model the level of gas and pressure as real numbers that can fall or grow continuously. With the inclusion of dynamics, the net can now model scenarios that simpler nets couldn’t:</p>
<ul>
<li>
<p><strong>Pressure building in a tank with relief valve cycling.</strong> Boil-off gas fills the empty space above the liquid, the reduced space raises the pressure more so a fuller tank pressurises faster. When pressure reaches the relief setpoint (8 units), the valve opens, 0.4 units of liquid escape as gas and the pressure drops just below 8. If the tank is still nearly full, pressure climbs back to 8 and the valve opens again. This reveals a trade-off where a fuller tank means fewer stockouts but more wastage through vented product, resulting in higher supplier costs.</p>
</li>
<li>
<p><strong>Realistic durations from lognormal distributions.</strong> The dispatch kernel samples a journey time from a lognormal distribution and writes it onto the tanker token. Place dynamics counts the remaining time down by 1 per hour, and the arrival transition fires when it reaches 0. This gives each journey a reasonable minimum, a mode near the nominal hours, and a long tail for delays.</p>
</li>
</ul>
<p>To add further realism, the net models outages in supplier’s own liquid production as discrete events. The production plant can experience an outage, on average every 90 hours (frequency exaggerated for visibility in simulation) and takes 24 hours to restart. While it is down, deliveries are dispatched from a more distant plant and increases each journey time by almost double.</p>
<p>This model captures the physical behaviour that the simpler nets cannot: tanks drain smoothly, pressure builds and vents in cycles, and journey times have realistic distributions rather than memoryless exponentials. What it does not capture is variability in the environment: customers always draw products at a constant contracted rate, and pressure boil-off does not vary with temperature.</p>
<h2 id="truck-fleet-maintenance">Truck fleet maintenance</h2>
<p>SDCPNs can be used for truck fleet operators to solve their maintenance problem, in deciding when and where they should service each vehicle. The service must occur early enough to prevent a breakdown on the road, and late enough not to waste maintenance capacity. The maintenance schedule must ensure deliveries are still completed within the agreed window.</p>
<figure class="petrinaut wide">
<iframe class="petrinaut-frame" src="https://demo.petrinaut.org/embed/examples/truck-fleet-predictive-maintenance" title="Truck fleet predictive maintenance. Open in Petrinaut" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen></iframe>
<figcaption>
<p>Truck fleet predictive maintenance. <a href="https://demo.petrinaut.org/examples/truck-fleet-predictive-maintenance">Open in Petrinaut</a></p>
</figcaption>
</figure>
<p>In this example, the fleet operator has 8 trucks over three route classes: motorway (420 km, flat), urban (180 km, stop-start), and mountain (260 km, steep gradients). Loads are posted to a freight board at stochastic rates; if no truck collects one within 10 hours, it goes to a competitor. Each delivery has a time window (2.2× driving time): missing the window incurs a 30% revenue penalty, missing it entirely (breakdown mid-route) means losing the load and paying for recovery.</p>
<p>The depot has 2 service bays, 2 technicians, and a stock of spare parts. A truck that reaches a wear threshold is serviced (5 hours, resets to new). A truck that breaks down at the roadside needs a recovery vehicle, a tow, and a longer repair (12 hours, only partially restores condition). Both compete for the same bays, technicians, and parts.</p>
<p>Each truck is modelled as a coloured token carrying 19 fields of continuous and discrete colouring. The key mechanisms are:</p>
<ul>
<li>
<p><strong>Evolution of three wear components per truck governed by deterministic dynamics.</strong> Brakes, engine, and tyres each degrade at different rates depending on the route. Brake wear accumulates 2.5× faster on mountain descents (heavy braking on gradients); engine wear increases when carrying load; tyre wear rises with road roughness and weather severity.</p>
</li>
<li>
<p><strong>Breakdown as stochastic firing driven by the weakest component.</strong> The roadside failure rate is set by whichever component is the most degraded. As degradation increases, the firing rate <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>λ</mi></mrow><annotation encoding="application/x-tex">\lambda</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.6944em;"></span><span class="mord mathnormal">λ</span></span></span></span> increases, so a truck with fresh brakes and fresh tyres but a worn engine fails at the engine’s rate.</p>
</li>
<li>
<p><strong>Road condition evolution governed by stochastic dynamics.</strong> The same diffusion mechanism that models ambient temperature in the industrial gas supply chain is also used here to model the variations in driving conditions that a truck encounters on any given trip. Two state variables on each truck token represent this road conditions: road severity which multiplies all wear rates and fuel consumption and speed factor which impacts travel speed and therefore journey time.</p>
</li>
<li>
<p><strong>Driver hours enforced through colour and guard.</strong> Each truck token carries a running hours field, and a guard that prevents dispatch once it exceeds the legal limit. EU Regulation (<a href="https://eur-lex.europa.eu/eli/reg/2006/561/oj/eng">561/2006</a>) limits drivers to 9 hours of continuous driving, so the model includes a compulsory rest stop at the depot for 11 hours before it can be dispatched again.</p>
</li>
</ul>
<p>The model helps the operator understand the interactions between servicing policy, workshop capacity and dispatch rules in relation to fleet profitability whilst accounting for variability in weather and roads. The main simplification is that it models one depot with identical trucks and generic parts. A real operator has multiple depots, mixed-age vehicles, component-specific spares, and demand that varies with season.</p>
<h2 id="semiconductor-wafer-fabrication">Semiconductor wafer fabrication</h2>
<p>A semiconductor foundry processes batches of wafers (wafer lots) through 28 steps using shared machines. The same machine group handles multiple steps in the sequence, for example the same lithography group is visited at layers 0, 4, 9, 12, 16, 20, and 24, so wafer lots at different stages compete for the same machines.</p>
<p>The main trade-off is between throughput and yield. Machines degrade as they work, but taking one offline for maintenance backs up the production. Deferring maintenance increases the risk for breakdown or slow accumulation of defects that only becomes visible at final inspection.</p>
<figure class="petrinaut wide">
<iframe class="petrinaut-frame" src="https://demo.petrinaut.org/embed/examples/semiconductor-fab-drift" title="Semiconductor fab drift. Open in Petrinaut" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen></iframe>
<figcaption>
<p>Semiconductor fab drift. <a href="https://demo.petrinaut.org/examples/semiconductor-fab-drift">Open in Petrinaut</a></p>
</figcaption>
</figure>
<p>For this use case we modelled 16 chambers across 4 machine groups (4 lithography, 6 etch, 4 furnace, 2 inspection), 3 product types (logic, memory, analog) arriving stochastically, a capacity limit of 50 lots in progress, and 3 technicians shared between planned and unplanned work.</p>
<p>Each lot token carries its product type, current layer, cumulative defects, age, and a customer due date. Each machine token holds data on its condition, particle count, hours since maintenance, machine group, qualification level, and batch counter. The degradation mechanisms use the following SDCPN features:</p>
<ul>
<li>
<p><strong>Machine degradation is modelled as deterministic dynamics</strong> that accelerate with contamination. When the machine crosses a threshold (default 0.85) it triggers preventive maintenance. The breakdown rate grows exponentially with condition, so a machine at 0.9 fails 8 times faster than a fresh one.</p>
</li>
<li>
<p><strong>Microscopic particle contamination as stochastic dynamics.</strong> Contamination fluctuates randomly but trends upward the longer a machine runs without maintenance.</p>
</li>
<li>
<p><strong>Per-chamber process drift as stochastic dynamics.</strong> Each chamber’s process accuracy varies independently via a second diffusion process. Drift in either direction from zero increases defect rates. Maintenance recalibrates the chamber, but calibration is imperfect and each reset samples a small residual error. This means two chambers on the same tool can produce different defect rates even at identical condition and particle levels.</p>
</li>
<li>
<p><strong>Defects at each step are sampled from a distribution when the processing transition fires.</strong> The mean depends on the machine’s current condition, its particle count and process drift. A clean, well-calibrated chamber deposits few defects; a degraded, contaminated or drifted chamber deposits many. Defects accumulate across all 28 layers but the number of defects is only checked at final inspection.</p>
</li>
</ul>
<p>To model the dispatch mechanism as close to real-world fabrication as possible, the net enforces all four of the below constraints simultaneously:</p>
<ul>
<li>
<p><strong>Machine-specific qualification.</strong> Not every machine can run every product. Each machine carries a bitmask encoding which product types it is certified for.</p>
</li>
<li>
<p><strong>Chamber-level recipes.</strong> Processing time depends on the product being made. For example, a furnace step takes 5 hours at baseline; analog lots take 15% longer, memory lots 15% shorter. The same applies to lithography and etch steps.</p>
</li>
<li>
<p><strong>Batch processing for furnaces.</strong> Furnace steps require loading multiple lots before the chamber fires. Lots destined for a furnace layer enter a batch queue and are loaded one at a time into an available furnace chamber. In the model the furnace only starts when the batch reaches 4 lots, or after 3 hours if fewer are available.</p>
</li>
<li>
<p><strong>Dynamic priority from customer due dates.</strong> Each lot arrives with a due date. Between dispatch events, the lot’s priority escalates continuously via dynamics. Priority is recalculated every 2 hours based on remaining time to deadline. A lot close to its due date is dispatched ahead of a lot with time in hand. Lots that exceed their due date by 30 hours trigger a deadline renegotiation, and the due date extends by one full cycle time and priority resets, representing the real-world practice of agreeing a new delivery window with the customer.</p>
</li>
</ul>
<p>The model can help fabrication managers understand the interaction between maintenance policy, chamber calibration, batch sizing and in progress capacity in relation to yield and on-time delivery. The main simplifications are that each chamber processes one lot at a time and lots cannot be split for partial rework (lot-splitting). This means the model’s absolute throughput figures are lower than a real foundry’s, but relative comparisons between scenarios remain valid because all scenarios share the same simplification. Modelling lot-splitting would reduce the cost of contamination events by allowing partial recovery as a secondary effect, but does not change the fundamental question of when to maintain.</p>
<h2 id="beyond-simulation-guarantees-and-probabilistic-claims">Beyond simulation: guarantees and probabilistic claims</h2>
<p>Running a simulation shows what’s expected to happen under a specific set of conditions. However, simulations don’t show what happens under untested conditions, nor is it guaranteed that simulations which are run are in any way representative of the real world. This section covers two kinds of claims that go beyond individual runs: structural guarantees that follow from the net’s topology alone, and probabilistic claims built on sampling. Other parts of the SgAI programme are developing methods for reasoning formally about complex, compositional SDCPN models at scale; here we focus on what the net structure and simulation-based methods already provide.</p>
<h3 id="structural-guarantees">Structural guarantees</h3>
<p>These come from the net’s topology, arc weights and connections:</p>
<ul>
<li>
<p><strong>Reachability</strong>. Can the system ever reach a specific state? For example: “is there any sequence of events that results in the tank being empty with no order on its way?” The check works by building the full graph of every state the net can reach from its starting state by firing every enabled transition and recording each new state. If the target state appears in that graph, it is reachable. If it does not appear, it is proven unreachable from the given starting state regardless of timing or ordering.</p>
</li>
<li>
<p><strong>Coverability</strong>. Reachability asks whether the net can reach a specified marking: the exact number and types of tokens in every place. Coverability asks whether the net can reach a marking that contains at least what you specify, while allowing additional tokens in any place. For example it answers the question, “Can there be at least 10 failed machines?&quot;, without specifying token counts for the other places.</p>
</li>
<li>
<p><strong>Boundedness</strong>. For any place in the net, you can determine the highest number of tokens it can ever hold by scanning every state in the full state graph and recording the maximum token count seen at each place. If the graph is finite, that maximum is the proven bound and can never be exceeded in any execution. If the graph cannot be completed (because some place grows without limit), the net is unbounded at that place, which usually signals a modelling error or a missing constraint. Conservation is a special case, where if a set of places always sums to the same total (e.g. items in storage + items in transit = total inventory), then each place in that set is bounded.</p>
</li>
<li>
<p><strong>Liveness</strong>. Can every transition fire at least once? A transition that never appears in the state graph is dead, either from a modelling error or a mechanism that is unreachable by design. Deadlock is the extreme case: no transition can fire and the system freezes.</p>
</li>
</ul>
<p>These checks require a finite state space. Once tokens carry real-valued data (continuous levels, pressures, temperatures), the state graph cannot be exhaustively checked and these proofs do not apply directly. Extending formal guarantees to models with continuous state and stochastic dynamics is an open research problem, and one of the reasons the ARIA Safeguarded AI programme is investing in this formalism.</p>
<h3 id="probabilistic-claims">Probabilistic claims</h3>
<p>Once the model includes randomness, it produces distributions rather than single answers. An SDCPN produces probabilistic results differently from a conventional simulator in two ways:</p>
<ul>
<li>
<p><strong>The randomness is formal and inspectable.</strong> Firing rates, lognormal durations, diffusion terms etc. are explicit components of the model specification. They can be inspected so a reviewer can read exactly what distribution governs each event. In a conventional simulator, stochastic behaviour typically lives in code scattered across event handlers.</p>
</li>
<li>
<p><strong>Rare-event probabilities can be quantified.</strong> Some failures are too rare to observe in ordinary Monte Carlo, estimating a probability of <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>10</mn><mrow><mo>−</mo><mn>9</mn></mrow></msup></mrow><annotation encoding="application/x-tex">10^{-9}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.8141em;"></span><span class="mord">1</span><span class="mord"><span class="mord">0</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.8141em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="katex-sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">−</span><span class="mord mtight">9</span></span></span></span></span></span></span></span></span></span></span></span> would need billions of runs. SDCPNs support acceleration methods (<a href="https://doi.org/10.1109/acc.2011.5991305">importance sampling</a>, <a href="https://doi.org/10.1201/9781420008548.ch10">interacting particle systems</a>) that exploit the net’s structure (strong Markov property) to estimate these probabilities efficiently.</p>
</li>
</ul>
<h3 id="tooling-for-scalable-oversight">Tooling for scalable oversight</h3>
<p><a href="https://hash.ai/">HASH</a> leads the “Interaction Paradigms” technical area (TA1.3) of the <a href="https://aria.org.uk/opportunity-spaces/trust-everything-everywhere/safeguarded-ai/">SgAI programme</a>. As part of this work, we’ve developed tooling to enable not only the modelling and simulation of complex systems as SDCPNs, but also AI that assists users in capturing their understanding of a domain while ensuring information relevant to a model or experiment isn’t missed or misrepresented. The same AI is also usable to help users understand complicated models developed by other people (or AI), and enables the precision editing, customisation, and hierarchical use of existing models. This open-source tooling is available at <a href="https://petrinaut.org">petrinaut.org</a>, as well as integrated into HASH’s semantic graph platform (<a href="https://app.hash.ai">app.hash.ai</a>). All of the interactive examples embedded in this post were built and run from the <a href="https://demo.petrinaut.org/">Petrinaut demo</a> website.</p>
<h2 id="conclusion">Conclusion</h2>
<p>As we’ve shown in this post, SDCPNs can be applied to virtually any cyber-physical domain: from industrial gas supply and truck fleet maintenance through to semiconductor fabrication, and the other use-cases we’re exploring as part of SgAI — with our own work predominantly focused on biopharmaceutical and chemicals manufacturing and logistics.</p>
<p>Each feature from the SDCPN formalism adds something new:</p>
<ul>
<li>
<p>A plain Petri net finds logical and structural failures like deadlocks</p>
</li>
<li>
<p>Stochastic firing adds timing, so the model can quantify how often events like stockouts occur.</p>
</li>
<li>
<p>Colour makes identity visible so fleet composition or product routing can be modelled.</p>
</li>
<li>
<p>Continuous dynamics replace discrete approximations with differential equations for flows like gas levels, machine degradation and contamination.</p>
</li>
<li>
<p>Stochastic dynamics helps to add the necessary real world noise, like environmental variability to the model.</p>
</li>
</ul>
<p>Not only can SDCPNs represent these various interactions, but it can quantify the frequency, costs and conditions that trigger them, too. This kind of quantification is what will enable us to move away from a world of weak evals and assumptions to one in which AI can be used as a real-time decision maker in safety-critical, cyber-physical systems, backed by evidence and verification.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[alifib, the language of higher-dimensional diagrams]]></title>
      <link>https://aria-sgai-notebook.inkandswitch.com/alifib/</link>
      <guid isPermaLink="true">https://aria-sgai-notebook.inkandswitch.com/alifib/</guid>
      <pubDate>Thu, 27 Aug 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[alifib is an experimental programming language and interactive proof assistant in which programs <em>are</em> diagrams — not illustrations of programs, but the syntax itself, the internal representation, and the thing that is checked and run.]]></description>
      <content:encoded><![CDATA[<p>As part of ARIA’s Safeguarded AI programme, I have been leading the development of
<strong>alifib</strong>, an experimental programming language and interactive proof
assistant founded on higher-dimensional rewriting. In alifib, programs are
diagrams: not illustrations of programs, but the syntax itself, the
internal representation, the thing that is checked and run. This notebook post
is an attempt to say what that means and what it is for. The proof assistant runs
in a browser at <a href="http://compose.ee/alifib">http://compose.ee/alifib</a>, with nothing to install, and the
reader may want to keep it open beside the text.</p>
<figure class="wide">
<img src="https://aria-sgai-notebook.inkandswitch.com/alifib/img/01-interface-overview.png" alt="The alifib web interface: three panes side by side. On the left, a code
editor with an .ali file open, syntax-highlighted. In the middle, a REPL pane
with a session-setup form (a type selector, source and target fields, a Start
button). On the right, a visualisation pane rendering a string diagram
(coloured nodes joined by wires) above a list of available
rewrites.">
<figcaption>
<p>The web interface, with an editor, an interactive session, and a live
string-diagram visualisation side by side.</p>
</figcaption>
</figure>
<h2 id="from-terms-to-diagrams">From terms to diagrams</h2>
<p>Nearly every programming language represents a program as a <em>term</em>, a
tree-like structure. The tree records what the program <em>is</em>; how it <em>runs</em>
is settled elsewhere—by reduction, by evaluation, by whatever mechanism
an interpreter or compiler chooses—outside the syntax, and taken on faith.</p>
<p>Term syntax also makes a quiet assumption: that data may be copied and
discarded at will. A variable may appear twice in a term, or never. Much of
computation is not like that—a resource consumed cannot be reused, a message
sent cannot be unsent—and category theory has long had a syntax for such
situations: <em>string diagrams</em> for monoidal categories, that is, nodes joined by
wires, each wire carrying a value, exactly once, from one operation into the
next. Terms are the special case in which every wire comes with copying and
discarding operations.</p>
<p>The second step is to go up in dimension. A string diagram is already a
two-dimensional object, having besides its nodes and wires the regions of the
plane that the wires bound. Once regions, wires, and nodes are counted as
cells of dimension 0, 1, and 2, there is no reason to stop counting. A
<em>pasting diagram</em> has cells of every dimension, each bounded by an input and
an output face: a point, an arrow between points, a filled surface between two
paths of arrows, a solid between two such surfaces. Each level, moreover,
reads as a rewrite of the level below—an arrow carries its source to its
target, a 2-cell carries one path of arrows into another—so that every storey of the
tower, however high one climbs, is the same kind of object. (The counting is
by role rather than by drawn shape: the string diagram is the Poincaré dual of
a two-dimensional pasting diagram, with regions standing for points, and nodes
for 2-cells.)</p>
<p>This is the entire ontology of the language: one kind of thing, the diagram,
and one non-trivial operation upon it, <em>subdiagram search</em>, which finds a
diagram sitting inside another. The whole rests on a single observation, the
founding principle of higher-dimensional rewriting:</p>
<blockquote>
<p>a computation on <em>n</em>-dimensional data is itself (<em>n</em>+1)-dimensional data.</p>
</blockquote>
<p>In every other model of computation that I know of, a program and a run of that
program are objects of different kinds: usually, a piece of syntax on one side, a
sequence of machine configurations on the other. Here they are the same kind
of object, one dimension apart, and the run is data again, ready in its turn
to be computed on.</p>
<h2 id="an-alphabet-of-two-letters">An alphabet of two letters</h2>
<p>A type in alifib is a diagrammatic signature, a collection of building blocks
of increasing dimension from which diagrams can be assembled. Let us look at
a simple 2-dimensional example, essentially an encoding of a string rewrite
system. Here is a type with a point <code>pt</code>, and two arrows <code>a</code>, <code>b</code> from the
point to itself, so that words, that is, strings of <code>a</code>s and <code>b</code>s, are
one-dimensional diagrams. A rule of grammar for these words is nothing but a
two-dimensional generator:</p>
<pre><code class="language-ali"><div class="code-line"><span class="token atrule">@Type</span></div><div class="code-line"><span class="token class-name">Letters</span> <span class="token operator">&lt;&lt;=</span> <span class="token punctuation">{</span></div><div class="code-line">    pt<span class="token punctuation">,</span></div><div class="code-line">    a<span class="token punctuation">:</span> pt <span class="token operator">-></span> pt<span class="token punctuation">,</span></div><div class="code-line">    b<span class="token punctuation">:</span> pt <span class="token operator">-></span> pt<span class="token punctuation">,</span></div><div class="code-line"></div><div class="code-line">    <span class="token comment">(* a rewrite rule is just a 2-dimensional generator *)</span></div><div class="code-line">    swap<span class="token punctuation">:</span> a b <span class="token operator">-></span> b a</div><div class="code-line"><span class="token punctuation">}</span></div></code></pre><p><code>swap</code> rewrites the pattern <code>a b</code>, wherever it occurs inside a word, into
<code>b a</code>. Note what is missing: there is no separate register in which
“evaluation rules” are declared, no computation mechanism external to the
type. The rule and the data it acts on are generators of one type, a dimension
apart. A point, two letters, and one rule are very little, but they are
enough to compute with.</p>
<figure>
<img src="https://aria-sgai-notebook.inkandswitch.com/alifib/img/02-letters-evaluate.png" alt="The editor pane with the Letters type as above; the Evaluate button has just
been pressed, and the output area below lists the elaborated type with its
generators pt, a, b, swap, each annotated with its dimension and
boundaries.">
<figcaption>
<p>Evaluating the file. The interpreter checks that the boundary of every
generator is a well-formed diagram, and reports what it has understood.</p>
</figcaption>
</figure>
<h2 id="watching-it-run">Watching it run</h2>
<p>Begin an interactive session on the word <code>a b a b</code>. The visualisation pane
draws it as a string diagram, while the engine searches out every position at
which <code>swap</code> matches, and lists them. This is subdiagram search at work;
everything else in the system is bookkeeping around it, and everything one is
ever asked to trust reduces to it.</p>
<figure>
<img src="https://aria-sgai-notebook.inkandswitch.com/alifib/img/03-session-rewrites.png" alt="A rewrite session in progress. The visualisation pane shows a horizontal
string diagram: four wires in sequence labelled a, b, a, b. Below it, a
rewrite list with two entries, each naming the rule swap and the position at
which it matches. The second occurrence of &quot;a b&quot; in the diagram is highlighted
in colour, because the pointer is hovering over the corresponding entry in the
list.">
<figcaption>
<p>Hovering over an entry highlights the subdiagram that the rewrite would
consume, and clicking applies it.</p>
</figcaption>
</figure>
<p>Each click applies one rewrite. The session, however, is not mutating a
string, as a text editor would: it is building a two-dimensional diagram, one
<code>swap</code> cell pasted on at each step, and the word on screen is only the
frontier of the construction. Stop at any point and switch the visualisation
from the step view to the proof view: everything that has happened is there,
as a single diagram.</p>
<figure>
<img src="https://aria-sgai-notebook.inkandswitch.com/alifib/img/04-proof-trace.png" alt="The proof view after three steps: a two-dimensional string diagram read
bottom-to-top. Along the bottom boundary, the wires a, b, a, b; along the top,
b, b, a, a; in between, three beads, each an application of swap, joined by
wires that trace how each letter moved through the computation. The REPL pane
alongside records the session history: step 1, step 2, step 3, each with the
rule applied.">
<figcaption>
<p>The trace is a 2-diagram whose input boundary is where the computation began
and whose output boundary is where it ended, with everything in between
retained. <code>store</code> gives it a name, after which it may be pasted into other
diagrams like any other term.</p>
</figcaption>
</figure>
<p>It is worth pausing on that diagram. It is not a log of the computation; it
<em>is</em> the computation, a first-class term of the language, which can be
inspected, replayed, pasted into larger diagrams, and acted upon by
higher-dimensional computations in its turn. If one only wants the answer,
one asks for its output boundary.</p>
<h2 id="computational-transparency">Computational transparency</h2>
<p>Recall the Curry–Howard correspondence, according to which programs are
proofs: a well-typed term certifies, by existing, that the program it denotes
is correctly formed. The correspondence, however, speaks only of the program.
A term also carries an equational theory—a notion of when two terms are
“the same”—and this theory, the very mechanism by which programs run, is
meta-theoretical: assumed to exist, assumed to be computable, and in practice
entrusted to the compiler. One may certify the program inside the language; to
certify its execution, one must trust something outside it.</p>
<p>With diagrams, this boundary disappears. The rules by which a type computes
are generators of the type itself, written down beside the ordinary ones,
and a run of a program is a higher term—exactly the diagram built in the
session above.</p>
<blockquote>
<p>Well-typed terms are verified programs.
Well-typed <em>higher</em> terms are verified executions.</p>
</blockquote>
<p>I call this principle <strong>computational transparency</strong>, and it is alifib’s
reason to exist. A verified program is not yet a verified run; between the two
stands the whole tower of compilers and machines, and it is there that
guarantees tend to evaporate. Our concern in this programme is with what a
system <em>did</em>, rather than with what it was meant to do, and transparency is
that concern made into syntax: the object one examines is the run itself, not
a report of the run.</p>
<p>There is a consequence worth stating on its own. Since the rules by which a
type computes are its own generators, each type is its own computational
universe: its generators create, at the same time, the space in which
computations happen, the data on which they act, and the computations
themselves; nothing arrives from outside. A type expressive enough to house
a Turing machine is still, in this sense, a sandbox, accounted for cell
by cell by a kernel small enough to be read.</p>
<p>None of this is confined to two-letter alphabets. <code>examples/TRS.ali</code> sets up
term rewriting systems in general, their terms two-dimensional diagrams and
their runs three-dimensional ones, and <code>examples/BinaryNat.ali</code> instantiates
it with binary arithmetic, so that</p>
<pre><code class="language-ali"><div class="code-line"><span class="token atrule">@BinaryNat</span></div><div class="code-line"><span class="token keyword">let</span> d <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">run</span> <span class="token keyword">auto</span> <span class="token keyword">on</span> <span class="token class-name">Eval</span><span class="token punctuation">.</span><span class="token punctuation">(</span><span class="token keyword">run</span> <span class="token keyword">auto</span> <span class="token keyword">on</span> parse <span class="token string">"57 * (12 + 30)"</span> <span class="token keyword">end</span><span class="token punctuation">)</span><span class="token punctuation">)</span></div></code></pre><p>binds <code>d</code> not to the number 2394 but to a witness of the entire computation,
whose output boundary is 2394. Since parsing, evaluation, and execution are
all the same kind of diagram, parsing itself turns out to be functorial—the
parse map sends parse traces of arithmetic expressions to the diagrams they
denote—a remark that I will not develop here, beyond noting that those two
files carry it out.</p>
<h2 id="eckmann-hilton-by-hand">Eckmann–Hilton, by hand</h2>
<p>Because equations are <em>directed</em> cells, equational reasoning in a
higher-categorical structure is the same activity as running a machine. The
classic Eckmann–Hilton argument says that two endomorphisms <code>a</code>, <code>b</code> of an
identity must commute. In <code>examples/EckmannHilton.ali</code> the setting is built
over a <code>Bicategory</code> module—itself written in ordinary alifib, its structural
laws oriented into pairs of cells one dimension up—and the claim is stated
as a map into the module’s type of equations, with two images withheld:</p>
<pre><code class="language-ali"><div class="code-line"><span class="token atrule">@EckmannHilton</span></div><div class="code-line"><span class="token keyword">let</span> <span class="token keyword">total</span> <span class="token class-name">Commutativity</span> <span class="token operator">::</span> <span class="token class-name">Equation</span> <span class="token operator">=</span> <span class="token punctuation">[</span></div><div class="code-line">    lhs <span class="token operator">=></span> a b<span class="token punctuation">,</span></div><div class="code-line">    rhs <span class="token operator">=></span> b a<span class="token punctuation">,</span></div><div class="code-line">    dir <span class="token operator">=></span> <span class="token operator">?</span><span class="token punctuation">,</span></div><div class="code-line">    inv <span class="token operator">=></span> <span class="token operator">?</span></div><div class="code-line"><span class="token punctuation">]</span></div></code></pre><p>Each <code>?</code> is a <em>hole</em>: an image left open, to be filled interactively. The two
fills are the proof: a pair of three-dimensional diagrams assembled by
clicking through rewrites exactly as before, in which <code>a</code> and <code>b</code> slide past
one another. Textbooks give this argument as a sequence of pictures, and the
sequence built here is scarcely longer; the difference is that every move now
exists as a cell, checked and retained, where the pictures rely on the
reader’s goodwill to interpolate between them.</p>
<figure>
<img src="https://aria-sgai-notebook.inkandswitch.com/alifib/img/05-eckmann-hilton.png" alt="A hole-filling session on EckmannHilton. The visualisation shows a string
diagram with two beads labelled a and b sitting on wires of the identity
1-cell, mid-proof: b has slid past a and now stands to its left. The rewrite
list offers the structural moves available at this step. The REPL pane shows
the session was begun with &quot;fill 0&quot; and displays the goal: rewrite a b into
b a.">
<figcaption>
<p>Filling the hole <code>dir =&gt; ?</code>. The proof that <code>a b</code> rewrites to <code>b a</code> is
assembled move by move; <code>done</code> splices the finished diagram into the map.</p>
</figcaption>
</figure>
<h2 id="four-readings">Four readings</h2>
<p>Beneath the syntax, an alifib type is a <em>finite directed complex</em>: a finite
family of cells graded by dimension, each carrying a partition of its boundary
into an input half and an output half. The combinatorics—the <em>molecules</em> of
my book, <em>Combinatorics of higher-categorical diagrams</em>—descend from
far-sighted work of Richard Steiner in the early 1990s, in a lineage that
runs back through Ross Street to the early days of higher category theory.
What holds the construction together is <em>topological soundness</em>: every such complex
is realised by an honest cell complex with exactly one cell per generator, so
the combinatorics never drift from the geometry they claim to describe.</p>
<p>The reward is that one object may be read, faithfully, in four ways at once:
as a piece of directed topology; as a presentation of a higher category; as a
higher-dimensional rewrite system; and as a directed higher inductive type,
with constructors between constructors. Each reading is a way of using the
language—one may reason equationally in monoidal categories and
bicategories, specify abstract machines whose every run is a witnessed trace,
or describe a space and ask for its homology at the prompt—and the readings
illuminate one another. In <code>examples/Bicategory_examples.ali</code>, the walking
equivalence (a morphism invertible up to invertible 2-cells) sits beside the
walking <em>adjoint</em> equivalence, which adds the two triangle equations of an
adjunction. Asked for their homology, the interpreter reports <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mn>2</mn></msub><mo>=</mo><mi mathvariant="double-struck">Z</mi></mrow><annotation encoding="application/x-tex">H_2 = \mathbb{Z}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0813em;">H</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0813em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="katex-sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:0.6889em;"></span><span class="mord mathbb">Z</span></span></span></span> for the
first and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mn>2</mn></msub><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">H_2 = 0</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.8333em;vertical-align:-0.15em;"></span><span class="mord"><span class="mord mathnormal" style="margin-right:0.0813em;">H</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3011em;"><span style="top:-2.55em;margin-left:-0.0813em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="katex-sizing reset-size6 size3 mtight"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="katex-base"><span class="katex-strut" style="height:0.6444em;"></span><span class="mord">0</span></span></span></span> for the second: a hole in dimension two, witnessing
topologically the classical fact that a bare equivalence is not coherent—and
that promoting it to an adjoint equivalence is precisely what fills the hole.</p>
<figure>
<img src="https://aria-sgai-notebook.inkandswitch.com/alifib/img/06-homology.png" alt="The REPL pane after two commands. &quot;homology Equivalence&quot; reports homology
groups by dimension with H_2 = Z; &quot;homology AdjointEquivalence&quot; reports the
same groups with H_2 = 0. Both types are loaded from
examples/Bicategory_examples.ali.">
<figcaption>
<p>A type is a cell complex; its holes, counted by dimension, are one command
away. Here the topology detects a categorical fact, the incoherence of a
non-adjoint equivalence.</p>
</figcaption>
</figure>
<h2 id="what-comes-next">What comes next</h2>
<aside class="move-up" style="--move-up: 0">The proof assistant is joint work with Alex Kavvos (University of Bristol),
with further contributions from Wessel de Weijer; the algorithms at the
interpreter’s core were developed with Diana Kessler, and the mathematics it
rests on owes much to my ongoing work with Clémence Chanavat, both at Tallinn.</aside>
<p>alifib today is a working system: a batch interpreter, a terminal REPL, the
browser interface pictured throughout this post, a line-protocol daemon for
editors, and an MCP server offering the same engine, tool by tool, to AI
agents. The last deserves a word. In alifib, building a proof is search, and
search is untrusted, whether a human is clicking or a model is calling; what
is trusted is the kernel that accepts or rejects each step. It does not matter,
then, how capable or how erratic the model is: nothing it produces enters a
type except through the same check that governs a human hand. This division
of labour—an inventive but untrusted proposer set against a small checker—will
sound familiar to readers from the Safeguarded AI programme.</p>
<aside class="move-up" style="--move-up: 0">The name <em>aflib</em> is borrowed from a song on Robert Wyatt’s <em>Rock Bottom</em>: a love song
written in a private version of the English language.</aside>
<p>alifib can be tried at <a href="http://compose.ee/alifib">http://compose.ee/alifib</a>, and its source lives at
<a href="https://github.com/ahadziha/alifib">https://github.com/ahadziha/alifib</a>. A paper giving a full account of the
language and its interpreter is currently under review.</p>
<p>Further off, modules will acquire higher cells of their own (a module is
already, in disguise, a complex whose points are types); a strategy language,
where the effectful part of the runtime will live, I/O included,
is being worked out with Wessel de Weijer; and a small family of type
constructors—duals, suspensions, Gray products, joins—will make globes,
oriented simplices, and oriented cubes available structurally, rather than as
constructions made by hand.</p>]]></content:encoded>
    </item>
    <item>
      <title><![CDATA[Why Supply Chains Are an Ideal Testbed for Safeguarded AI]]></title>
      <link>https://aria-sgai-notebook.inkandswitch.com/scm/</link>
      <guid isPermaLink="true">https://aria-sgai-notebook.inkandswitch.com/scm/</guid>
      <pubDate>Tue, 07 Apr 2026 00:00:00 GMT</pubDate>
      <description><![CDATA[Supply chains pair interrelated decisions with high-consequence outcomes, which is what makes them an ideal proving ground for Safeguarded AI — a world model that predicts an action’s consequences, an explicit specification of acceptable outcomes, and a verifier that can establish the system satisfies it.]]></description>
      <content:encoded><![CDATA[<p>Supply chains are inherently complex systems, where simple ‘optimisations’ may have significant downstream consequences, requiring a high level of assurance. A planner might reroute a shipment, switch supplier, adjust a production schedule, or release inventory—but each action can create knock-on effects several tiers away that may be hard for a human to accurately predict, or even reasonably be aware of. In biopharmaceutical supply chains, these consequences extend beyond cost and service levels to regulatory compliance, product quality, and product safety – ultimately resulting in an impact on patient health as well as that of the business.</p>
<p>Imagine a pandemic-scale surge in vaccine demand which coincides with labour shortages at a factory and constrained cold-chain capacity. Planners must decide which sites produce which medicines, how scarce inventory and freight are allocated, and which shipments are rerouted—without leaving one region short or displacing another critical product. A locally sensible choice can create failures several tiers downstream. Even in routine operations, the recent <a href="https://hash.ai/">HASH</a>/<a href="https://aria.org.uk/">ARIA</a> <a href="https://aria.org.uk/media/fapdst2k/hash-sails-public-report.pdf">Safeguarded AI report on biopharmaceutical supply chains</a> cites an estimated €4 billion in annual vaccine cold-chain waste in Europe and more than $35 billion in annual losses from temperature excursions across the global pharmaceutical industry.</p>
<p>That combination of interrelated decisions and high-consequence outcomes makes many <a href="https://hash.ai/apps/supply-chain">supply chain applications</a> ideal use cases for Safeguarded AI (SgAI). The report explains that safe AI decision-making requires three things: a world model capable of predicting an action’s consequences, an explicit specification of acceptable outcomes, and a verifier that can establish whether the resulting system satisfies that specification.</p>
<h2 id="why-supply-chains-need-safeguarded-ai">Why supply chains need Safeguarded AI</h2>
<h3 id="supply-chains-are-complex-but-structurally-modelable">Supply chains are complex but structurally modelable.</h3>
<p>Facilities, suppliers, transport lanes, inventories, production processes, inspections, and orders form a network of concurrent events and constrained resources. Uncertainty enters through demand, lead times, equipment failures, quality events, and disruptions. The system is difficult to reason about informally, yet its important entities and interactions can still be represented <a href="https://topos.institute/blog/2024-10-31-declarative-models-and-collaborative-modeling/">declaratively</a>.</p>
<h3 id="many-safety-requirements-are-concrete">Many safety requirements are concrete.</h3>
<p>Consider a temperature-sensitive vaccine shipment that is delayed while its container temperature trends towards its validated limit. The world model might track the batch’s location, temperature history, remaining shelf life, predicted weather, route-time distributions, destination demand, and available replacement stock.</p>
<p>The optimisation variables are the actions available to the controller: continue the journey, pause it in cold storage, select another route or transport mode, or reallocate replacement inventory. The objective might be to minimise expected waste, delay, cost, and stock-out risk. The safety specification is more categorical: never deliver a batch that has failed or still requires quality assessment; never choose an intervention that increases excursion risk; never divert stock from a higher-priority destination to a lower-priority one; and never violate approved handling or transport constraints.</p>
<p>This separation between what should be optimised and what must never happen (certified up to probability <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow><annotation encoding="application/x-tex">\varepsilon</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="katex-base"><span class="katex-strut" style="height:0.4306em;"></span><span class="mord mathnormal">ε</span></span></span></span>) is central to SgAI.</p>
<h3 id="rare-long-tail-edge-cases-matter-enormously">Rare, long-tail edge cases matter enormously.</h3>
<p>Those specifications become most valuable when failures compound. A distribution plan that is safe under ordinary demand may become unsafe when a supplier failure, port closure, equipment outage, and demand spike occur together—as they might during the next pandemic-scale emergency. Historical testing can show that a policy usually performs well, but it cannot contain every consequential combination of events. SgAI instead asks whether a controller remains within its safety specification across the behaviours represented by the world model—including unlikely ones.</p>
<h2 id="modeling-supply-chains-with-petri-nets">Modeling supply chains with Petri Nets</h2>
<p><a href="https://en.wikipedia.org/wiki/Petri_net">Petri nets</a> are a natural candidate for building such models. Places can represent states or resources, transitions represent events or decisions, and tokens represent batches, orders, vehicles, or capacity. Coloured, timed, and stochastic extensions add product data, process durations, and uncertainty. The result is both executable and analysable: candidate policies can be simulated, while reachability, invariants, and related verification techniques identify forbidden states, deadlocks, or resource violations.</p>
<p>Stochastically and Dynamically Coloured Petri Nets (SDCPNs) go further by allowing token attributes to evolve as continuous stochastic processes. Semantically speaking, SDCPNs are equivalent to General Stochastic Hybrid Systems, which are expressive enough to enable the modeling of almost all cyber-physical systems (<a href="https://cdn.intechopen.com/pdfs/9186/InTech-Hybrid_state_petri_nets_which_have_the_analysis_power_of_stochastic_hybrid_systems_and_the_formal_verification_power_of_automata.pdf">Everdij &amp; Blom, 2010</a>). For example, in a pharmaceutical supply chain context, SDCPNs enable us to model the moving of a cold-chain batch discretely between supplier, vehicle, warehouse, and quarantine states – while its temperature exposure, remaining shelf life, or degradation risk evolves continuously – providing a compositional model of both operational logic and physical uncertainty.</p>
<p>But supply chains are more than a convenient demonstration domain. They offer consequential decisions, explicit safety boundaries, rich uncertainty, and measurable outcomes—all within systems that can be formally modelled. This is a sweet spot for SgAI to demonstrate its value: enabling AI to search for better decisions while mathematical assurance determines which decisions are safe enough to act on.</p>
<p>As part of the SgAI programme, the open-source <a href="https://petrinaut.org/">Petrinaut</a> library for working with SDCPNs has been developed, and now powers this real-world supply chain modeling and optimisation capability in the <a href="https://hash.ai/features">HASH platform</a>.</p>
<hr class="thin">
<p><em>This post was written by <a href="https://vilkinsons.com/">Dei Vilkinsons</a> from <a href="https://hash.ai/">HASH</a>, who lead TA1.3 of the Safeguarded AI programme. HASH’s work on the programme seeks to enable the accurate and complete modeling and specification of cyber-physical environments, and as part of TA2 they are leading efforts to apply the programme’s technology in a variety of real-world production contexts.</em></p>]]></content:encoded>
    </item>
  </channel>
</rss>