dsfb-semiotics-calculus 0.1.0

DSFB Structural Semiotics Calculus — typed residual sign framework, grammar FSM, endoductive operator, and provenance engine. Rust type-level realization of the DSSC formal calculus (Invariant Forge LLC, April 2026).
Documentation
  • Coverage
  • 100%
    92 out of 92 items documented0 out of 65 items with examples
  • Size
  • Source code size: 139.82 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.64 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 57s Average build duration of successful builds.
  • all releases: 57s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • infinityabundance

dsfb-semiotics-calculus

Invariant Forge LLC · v0.1.0 · April 2026 · Apache-2.0

Open In Colab Crates.io docs.rs

Crate status: zero warnings · zero errors · 13 artifacts generated by cargo run

Rust type-level realization of the DSFB Structural Semiotics Calculus (DSSC) — a typed algebraic framework for deterministic, non-interfering, auditable structural interpretation of residual trajectories. Every safety-case claim is enforced at the type level by the Rust compiler; no runtime configuration required.


Contents

  1. What is the DSSC?
  2. Formal type system
  3. Mathematical framework
  4. Provable properties
  5. Safety-case properties
  6. Quick start
  7. Using the library
  8. CLI artifact generator
  9. Full API reference
  10. Canonical figures
  11. Colab notebook
  12. Benchmark excerpt
  13. Citation
  14. IP notice

What is the DSSC?

The DSFB Structural Semiotics Calculus (DSSC) turns residual streams already produced by any incumbent monitoring system into deterministic, auditable, typed structural interpretations — without modifying, replacing, or accessing the system that produces them.

The key contrast with abduction-based diagnostics:

Approach Empty library behaviour Output type Audit trail
Abduction (FDC, ML, NN) Silent — no hypothesis Best match or nothing Black-box
DSSC / Endoduction Episode(Unknown, φ) — fully characterized Named motif or typed Unknown Complete derivation tree φ

The DSSC is an alphabet, not a dictionary. For an event it has never seen, it spells out the complete structural characterization, proves why it is there, and guarantees the spelling process will not affect the observed system.


Formal type system

The DSSC is a simply-typed calculus. The base types and their Rust realizations:

Type Notation Meaning Rust type
τ_r r(k) ∈ V Residual trajectory value f64 / Vec<f64>
τ_σ σ(k) = (‖r‖, ṙ, r̈) Residual sign triple ResidualSign
τ_E E ⊆ V Admissibility envelope AdmissibilityEnvelope
τ_g g ∈ {Adm, Bdy, Vio} Grammar state GrammarState
τ_m m ∈ M ∪ {Unknown} Motif descriptor Motif
τ_φ φ = (σ, g, α, range) Provenance tag ProvenanceTag
τ_h h: M ⇀ 𝒫(Σ*) Heuristics bank HeuristicsBank
τ_α α: T → ℝ ADD algebraic invariant String (serialized)

An observer configuration is the 4-tuple ξ = (r, σ, g, h) : τ_r × τ_σ × τ_g × τ_h. An episode is the pair (m, φ) : τ_m × τ_φ.

The term language primitives and their types:

sign(r, k)         : τ_σ          — residual sign at time k
env(λ, k)          : τ_E          — active envelope at time k
transit(g, σ, k)   : τ_g          — grammar transition
match(σ, g, h)     : τ_m × τ_φ   — motif matching
enduce(r, σ, g, h) : τ_m × τ_φ   — endoductive episode
augment(h, m, P)   : τ_h          — heuristics bank augmentation
fuse(h₁, h₂)       : τ_h          — cross-stream bank fusion

Mathematical framework

Residual sign triple (τ_σ)

Fix a normed space (V, ‖·‖) with V ≅ ℝⁿ. For a residual trajectory r : {0,…,T} → V, the residual sign at step k is the triple:

σ(k) = ( ‖r(k)‖,  ṙ(k),  r̈(k) )

where the discrete-time derivatives are:

ṙ(k) = r(k) − r(k−1)         (first derivative; drift)
r̈(k) = ṙ(k) − ṙ(k−1)        (second derivative; slew)

with ṙ(0) = 0, r̈(0) = r̈(1) = 0. The sign sequence of r is σ = (σ(0), …, σ(T)) ∈ Σ^(T+1), where Σ = ℝ≥0 × V × V.

use dsfb_semiotics_calculus::ResidualSign;

// σ(k) from scalar r(k), r(k−1), r(k−2)
let sign = ResidualSign::from_scalar(
    0.85,  // r(k)   → magnitude = 0.85, drift = 0.85 − 0.70 = 0.15
    0.70,  // r(k−1)
    0.50,  // r(k−2) → slew = 0.15 − 0.20 = −0.05
);

assert_eq!(sign.magnitude, 0.85);
assert!((sign.drift - 0.15).abs() < 1e-12);
assert!(sign.is_drifting_outward()); // drift > 0
Field Notation Meaning
magnitude ‖r(k)‖ Residual norm — always ≥ 0
drift ṙ(k) First derivative; positive = moving away from origin
slew r̈(k) Second derivative; rate of change of drift

Admissibility envelope (τ_E)

An admissibility envelope is a compact set E ⊆ V with 0 ∈ int(E) satisfying the uniform inner-ball condition:

B(0, ρ_min) ⊆ E ⊆ B(0, ρ_max),   0 < ρ_min ≤ ρ_max < ∞

The δ-band boundary layer (Definition 4.1 of the paper) is the annular region:

∂_δE = { v ∈ V : ρ_max − δ ≤ ‖v‖ ≤ ρ_max + δ }

with the constraint δ ∈ (0, ρ_min/4] enforced at construction. Classifying a residual magnitude against E yields one of three regions:

‖r(k)‖ < ρ_max − δ               → Interior   → grammar: Adm
ρ_max − δ ≤ ‖r(k)‖ ≤ ρ_max + δ  → Boundary   → grammar: Bdy
‖r(k)‖ > ρ_max + δ               → Exterior   → grammar: Vio

An envelope family {E_λ}_{λ∈Λ} satisfies regime monotonicity: λ₁ ≼ λ₂ ⟹ E_λ₁ ⊆ E_λ₂

use dsfb_semiotics_calculus::{AdmissibilityEnvelope, EnvelopeFamily};
use dsfb_semiotics_calculus::envelope::EnvelopeRegion;

// δ = 0.02 ≤ ρ_min/4 = 0.025 — constraint satisfied, constructor succeeds
let env = AdmissibilityEnvelope::new(0.1, 1.0, 0.02);

assert_eq!(env.classify(0.50), EnvelopeRegion::Interior);
assert_eq!(env.classify(0.99), EnvelopeRegion::Boundary);
assert_eq!(env.classify(1.05), EnvelopeRegion::Exterior);

// Multi-regime family (regime monotonicity: ρ_max increases)
let family = EnvelopeFamily::new(vec![
    AdmissibilityEnvelope::new(0.1, 0.8, 0.02),
    AdmissibilityEnvelope::new(0.1, 1.2, 0.02),
    AdmissibilityEnvelope::new(0.1, 2.0, 0.02),
]);

Grammar FSM (τ_g)

The DSSC grammar is a deterministic finite-state machine (DFSM) over alphabet Σ with:

  • State space: G = {Adm, Bdy, Vio}
  • Transition function: δ: G × Σ → G (total — defined for every input)
  • Initial state: Adm

The three SOS grammar rules define δ:

(Grammar-Adm):  r(k) ∈ int(E_ρ(k))             ⟹  g(k) = Adm
(Grammar-Bdy):  r(k) ∈ E_ρ(k) \ int(E_ρ(k))   ⟹  g(k) = Bdy
(Grammar-Vio):  r(k) ∉ E_ρ(k)                  ⟹  g(k) = Vio

The persistence counter K(k) = max{j ≥ 0 : g(k) = g(k−1) = … = g(k−j)} counts consecutive steps in the current grammar state.

use dsfb_semiotics_calculus::{AdmissibilityEnvelope, ResidualSign, GrammarFsm, GrammarState};

let env = AdmissibilityEnvelope::new(0.1, 1.0, 0.02);
let mut fsm = GrammarFsm::new(); // starts in Adm

let states: Vec<GrammarState> = vec![0.5_f64, 0.99, 1.05]
    .into_iter()
    .map(|r| {
        let sign = ResidualSign::from_scalar(r, 0.0, 0.0);
        fsm.step(&sign, &env)
    })
    .collect();

assert_eq!(states[0], GrammarState::Admissible);
assert_eq!(states[1], GrammarState::Boundary);
assert_eq!(states[2], GrammarState::Violation);

Theorem 3.1 (Determinism and Totality): For every well-typed configuration ⟨ξ, P⟩, there exists exactly one successor ⟨ξ', P'⟩. The grammar partitions residual positions exhaustively and exclusively — no undefined path exists. Rust enforces this: GrammarFsm::step returns GrammarState, never Option<GrammarState>.


SOS operational semantics

The full DSSC semantics is a small-step SOS over configurations ⟨ξ, P⟩ where ξ = (r, σ, g, h) is the observer state and P is the term program.

Sign-Eval:

  r(k) ∈ V,  k ≤ T
  ─────────────────────────────────────────────────────────────────────
  ⟨ξ, sign(r,k)·P⟩  →  ⟨ξ[σ ↦ (‖r(k)‖, r(k)−r(k−1), r(k)−2r(k−1)+r(k−2))], P⟩

Grammar-Adm:

  r(k) ∈ int(E_ρ(k))
  ──────────────────────────────────────────
  ⟨ξ, transit(g,σ,k)·P⟩  →  ⟨ξ[g ↦ Adm], P⟩

Grammar-Bdy:

  r(k) ∈ E_ρ(k) \ int(E_ρ(k))
  ──────────────────────────────────────────
  ⟨ξ, transit(g,σ,k)·P⟩  →  ⟨ξ[g ↦ Bdy], P⟩

Grammar-Vio:

  r(k) ∉ E_ρ(k)
  ──────────────────────────────────────────
  ⟨ξ, transit(g,σ,k)·P⟩  →  ⟨ξ[g ↦ Vio], P⟩

Enduce:

  ξ = (r, σ, g, h),   (m, φ) = ℰ(r, σ, g, h)
  ─────────────────────────────────────────────────
  ⟨ξ, enduce(r,σ,g,h)·P⟩  →  ⟨ξ, (m,φ)·P⟩

Endoductive operator (ℰ)

Definition 5.1 — Endoductive Operator: The endoductive operator ℰ is the partial function:

ℰ : T × Σ* × G* × τ_h  ⇀  τ_m × τ_φ
ℰ(r, σ, g, h) = (m, φ)

where m ∈ M is a motif descriptor drawn from the heuristics bank h (or the distinguished symbol Unknown), and φ is a provenance tag recording the complete derivation tree: sign evolution σ, grammar transition sequence g, and ADD invariants α that licensed the motif.

Endoduction as the fourth inference mode (contrast with the Peircean triad):

Mode Input required Output Core assumption
Deduction Rule + case Necessary conclusion Rule is complete
Induction Specific cases General rule Future resembles past
Abduction Observation + hypothesis library Best library match Library is sufficient
Endoduction Observation + structural organization Typed motif or Unknown+φ Structure encodes interpretable organization

Theorem 5.2 (Soundness): Every episode (m, φ) produced by ℰ is faithful to the input trajectory: m is derivable from (σ, g, α) via the deterministic DSSC rewriting rules.

Theorem 5.3 (Bounded Completeness): For any structurally detectable excursion, there exists a finite time k* such that ℰ either names a motif from h or returns (Unknown, φ) with the complete structural characterization (σ, g, α).

Corollary 5.4 (No Silent Failure): The DSSC observer never fails silently. Every structurally detectable excursion produces a named or explicitly typed episode with full provenance within the time bound of Theorem 5.3.

The Enduce trait encodes this in Rust — enduce() returns Episode, never Option<Episode>:

use dsfb_semiotics_calculus::{
    enduce::{Enduce, DefaultEnduce},
    ResidualSign, GrammarState, HeuristicsBank, Episode,
};

// DefaultEnduce: correct Day-One implementation — always returns Unknown+φ
let op = DefaultEnduce;
let signs = vec![ResidualSign::from_scalar(1.1, 0.9, 0.7)];
let path  = vec![GrammarState::Violation];
let bank  = HeuristicsBank::new();

// Returns Episode — never panics, never Option
let episode: Episode = op.enduce(&signs, &path, &bank, (0, 0), "");
assert!(episode.is_unknown()); // Unknown with full ProvenanceTag

Observer functor (𝒪: Traj → Ep)

Theorem 3.2 (Observer Functor): The DSSC observer defines a functor 𝒪: TrajEp:

  1. Well-definedness (SC-1): 𝒪 maps every trajectory to a unique episode sequence.
  2. Stationarity: Grammar transitions are Markovian; 𝒪 is time-shift invariant.
  3. Compositionality: 𝒪(r₁ · r₂) = 𝒪(r₁) ⊗ 𝒪(r₂) under episode-sequence concatenation ⊗.
  4. Purity (SC-2): Output depends only on input trajectory — no side effects.

Corollary (Non-Interference): The DSSC observer can be removed from any system without affecting that system's behavior. Rust enforces this structurally: Observer holds no &mut to the observed system — the borrow checker makes interference a compile error.


Composition operators

1. Hierarchical grammar fusion G₁ ⋈ G₂ (Definition 7.1)

The product FSM has state space G⁽¹⁾ × G⁽²⁾ and transition function δ⁽¹⁾ × δ⁽²⁾:

G₁ ⋈ G₂ = ( G⁽¹⁾ × G⁽²⁾,  δ⁽¹⁾ × δ⁽²⁾,  (g₀⁽¹⁾, g₀⁽²⁾) )

Proposition 7.1: If G₁ and G₂ are deterministic FSMs, G₁ ⋈ G₂ is a deterministic FSM.

2. Heuristics bank augmentation — monotone (Theorem 7.2)

augment(h, m, P) = h ∪ {m ↦ P}        if m ∉ dom(h)
                   h[m ↦ h(m) ∪ P]    if m ∈ dom(h)

No existing motif pattern is ever removed or narrowed. Rust enforces this: HeuristicsBank has no remove() method — monotonicity is structurally impossible to violate.

3. ADD rewriting commutativity (Theorem 6.2)

ADD rewriting commutes with grammar transitions modulo a regime-index update:

δ( g, Sign(wᵢ(r(k))), E_ρ'(k) )  =  wᵢ*(δ( g, Sign(r(k)), E_ρ(k) ))

where ρ'(k) ≻ ρ(k) is the regime-shifted envelope and wᵢ* is the identity on G.


Provable properties

Complete meta-property inventory established in the companion paper:

Property Statement SC Theorem
Determinism & Totality Every well-typed config reduces to a unique terminal SC-1 Thm 3.1
Observer Functor 𝒪: Traj → Ep is pure, stationary, compositional SC-2 Thm 3.2
Non-Interference Observer removal leaves system behavior unchanged SC-2 Cor 3.3
Structural Detectability Sustained outward drift exits envelope in time ≤ ⌈ρ_max/d⌉ Thm 4.1
Soundness of Endoduction Every episode is derivable from the observed trajectory SC-1 Thm 5.2
Bounded Completeness Every detectable excursion is named or typed Unknown in finite time SC-4 Thm 5.3
No Silent Failure Observer always produces a characterized output SC-5 Cor 5.4
Bank Monotonicity Augmentation never removes or shrinks existing patterns Thm 7.2
Grammar Invariance Adm state preserved under bounded inward ADD rewriting Thm 6.1
ADD Refines Envelopes ADD reachability bounds grammatical violation Thm 6.2
ADD–Grammar Commutation ADD rewriting commutes with grammar transitions (mod regime) Thm 6.3
Bounded Fragmentation Noise ε < ρ_min/2 → at most T·2ε/ρ_min grammar-state differences Thm 8.1
Graceful Degradation Heavy-tailed noise → linear growth in Unknown episodes, no crash SC-6 Lem 8.2
Deterministic Auditability Every episode carries a complete, replayable derivation tree SC-3 Thm 8.3
Meta-Preservation ADD∘DSFB composition preserves all properties SC-1–6 Thm 6.4
Day-One Value Empty-bank deployment produces valid, fully characterized episodes Prop 9.1

Bounded Fragmentation (Theorem 8.1): For trajectories r and r̃ with ‖r(k) − r̃(k)‖ ≤ ε for all k, where ε < ρ_min/2, the number of steps where their grammar states differ is:

N_diff  ≤  T · 2ε / ρ_min

Graceful Degradation (Lemma 8.2): Under heavy-tailed noise with Pr[‖w(k)‖ > ε] ≤ Cε⁻ᵅ:

E[N_frag]  ≤  T · 2·E[‖w‖] / ρ_min  +  T · C · ρ_min⁻ᵅ

More noise → more Unknown episodes with complete structural descriptors. No crash, no deadlock, no uncertified output.

Structural Detectability (Theorem 4.1): Let r start at r(0) ∈ int(E) with sustained outward drift d > 0. Then the grammar exits Adm within k* steps where:

k*  ≤  ⌈ ρ_max / d ⌉

Safety-case properties (SC-1 – SC-6)

Property Claim Rust enforcement mechanism
SC-1 Determinism enduce is a pure function of its inputs Return type Episode (not Option, not Result)
SC-2 Non-Interference Observer does not mutate observed system Observer holds no &mut to host state; borrow checker
SC-3 Auditability Every Episode carries a recoverable certificate Episode.provenance: ProvenanceTag always present
SC-4 Coverage Grammar classifies every timestep GrammarFsm::step is a total function
SC-5 No Silent Failure Unrecognized events are labelled, not dropped Motif::Unknown with complete ProvenanceTag — never None
SC-6 Graceful Degradation Impulsive inputs do not panic Unknown episodes produced; no unwrap() in hot path

Quick start

Add to your Cargo.toml:

[dependencies]
dsfb-semiotics-calculus = "0.1"

Minimal observer over a residual trajectory:

use dsfb_semiotics_calculus::{AdmissibilityEnvelope, HeuristicsBank, Observer};

fn main() {
    // ρ_min=0.1, ρ_max=1.0, δ=0.02  (δ ≤ ρ_min/4 = 0.025 ✓)
    let envelope = AdmissibilityEnvelope::new(0.1, 1.0, 0.02);

    // Day-One deployment — empty bank is valid (Proposition 9.1)
    let bank = HeuristicsBank::new();
    let mut observer = Observer::new(envelope, bank);

    let residuals = vec![0.1, 0.3, 0.5, 0.7, 0.85, 0.96, 1.1];
    let episodes = observer.observe_trajectory(&residuals);

    for ep in &episodes {
        println!("{}", ep);
        println!("  window:    {:?}", ep.provenance.step_range);
        println!("  violation: {}", ep.provenance.contains_violation());
    }
    // → Episode(Unknown, steps 0–6)
}

Day-One proposition (Prop. 9.1): An empty bank is operationally valid. The system characterizes structural events before any fault library is built. Every excursion produces Motif::Unknown with complete ProvenanceTag — usable audit data from the first deployment second.


Using the library

Envelope construction

use dsfb_semiotics_calculus::AdmissibilityEnvelope;

// Scalar single-regime envelope
let env = AdmissibilityEnvelope::new(
    0.1,   // ρ_min: inner exclusion radius (> 0)
    1.0,   // ρ_max: outer boundary
    0.02,  // δ: boundary-layer half-width (≤ ρ_min/4)
);

// The constraint δ ≤ ρ_min/4 is checked at construction.
// This panics with a descriptive message (deployment-time calibration error):
// let bad = AdmissibilityEnvelope::new(0.1, 1.0, 0.1); // panics: δ > ρ_min/4

Grammar FSM usage

use dsfb_semiotics_calculus::{AdmissibilityEnvelope, GrammarFsm, ResidualSign};

let env = AdmissibilityEnvelope::new(0.1, 1.0, 0.02);
let mut fsm = GrammarFsm::new(); // initialized to Adm

for (k, &r) in [0.4_f64, 0.6, 0.99, 1.05, 0.4].iter().enumerate() {
    let sign = ResidualSign::from_scalar(r, 0.0, 0.0);
    let state = fsm.step(&sign, &env);
    println!("k={k}: ‖r‖={r:.2}{state} (K={})", fsm.persistence());
}
// k=0: ‖r‖=0.40 → Adm (K=0)
// k=1: ‖r‖=0.60 → Adm (K=1)
// k=2: ‖r‖=0.99 → Bdy (K=0)
// k=3: ‖r‖=1.05 → Vio (K=0)
// k=4: ‖r‖=0.40 → Adm (K=0)

Observer — step-by-step

use dsfb_semiotics_calculus::{AdmissibilityEnvelope, HeuristicsBank, Observer};

let mut obs = Observer::new(
    AdmissibilityEnvelope::new(0.1, 1.0, 0.02),
    HeuristicsBank::new(),
);

let trajectory = [0.2_f64, 0.4, 0.7, 0.95, 1.05, 0.6, 0.3];
let mut prev = 0.0_f64;
let mut prev2 = 0.0_f64;

for (k, &r) in trajectory.iter().enumerate() {
    if let Some(episode) = obs.observe_step(r, prev, prev2, k) {
        println!("Episode at step {k}: {episode}");
        println!("  motif:        {:?}", episode.motif);
        println!("  grammar path: {:?}", episode.provenance.grammar_path);
    }
    prev2 = prev;
    prev = r;
}

Observer — batch trajectory

use dsfb_semiotics_calculus::{AdmissibilityEnvelope, HeuristicsBank, Observer};

let mut obs = Observer::new(
    AdmissibilityEnvelope::new(0.1, 1.0, 0.02),
    HeuristicsBank::new(),
);

// observe_trajectory handles drift/slew history internally
let residuals = vec![0.1, 0.3, 0.6, 0.9, 1.05, 0.7, 0.2,
                     0.15, 0.8, 1.2, 0.6, 0.3_f64];
let episodes = obs.observe_trajectory(&residuals);

println!("Episodes produced: {}", episodes.len());
for ep in &episodes {
    println!(
        "  {} — window {:?}, violation: {}",
        ep.motif, ep.provenance.step_range, ep.provenance.contains_violation()
    );
}

Bank augmentation

use dsfb_semiotics_calculus::{HeuristicsBank, bank::MotifPattern};

let mut bank = HeuristicsBank::new();

// Augment with a slow-ramp motif
bank.augment("SlowRamp", MotifPattern {
    name: "SlowRamp".into(),
    min_persistence: 3,        // ≥3 consecutive non-nominal steps
    requires_violation: true,  // must include confirmed Vio
    min_drift: 0.05,           // outward drift ≥ 0.05
});

// Augment with an impulsive spike motif
bank.augment("ImpulsiveSpike", MotifPattern {
    name: "ImpulsiveSpike".into(),
    min_persistence: 1,
    requires_violation: true,
    min_drift: 0.30,
});

// Bank has no remove() method — monotonicity (Thm 7.2) is structurally enforced
assert_eq!(bank.motif_count(), 2);

Custom endoductive operator

Implement the Enduce trait to provide domain-specific motif matching while inheriting the type-level totality guarantee:

use dsfb_semiotics_calculus::{
    enduce::Enduce,
    ResidualSign, GrammarState, HeuristicsBank, Episode, Motif,
    provenance::ProvenanceTag,
};

struct MyEnduce;

impl Enduce for MyEnduce {
    fn enduce(
        &self,
        signs: &[ResidualSign],
        grammar_path: &[GrammarState],
        bank: &HeuristicsBank,
        step_range: (usize, usize),
        add_descriptor: &str,
    ) -> Episode {
        // Must always return Episode (never Option) — totality is a type guarantee
        let max_drift = signs.iter().map(|s| s.drift).fold(f64::NEG_INFINITY, f64::max);
        let motif = if max_drift > 0.5 {
            Motif::named("RapidExcursion")
        } else {
            bank.match_episode(signs, grammar_path, grammar_path.len())
        };
        Episode::new(motif, ProvenanceTag::new(
            signs.to_vec(),
            grammar_path.to_vec(),
            add_descriptor,
            step_range,
        ))
    }
}

// Wire a custom operator into Observer
use dsfb_semiotics_calculus::{AdmissibilityEnvelope, Observer};

let mut obs = Observer::with_enduce(
    AdmissibilityEnvelope::new(0.1, 1.0, 0.02),
    HeuristicsBank::new(),
    MyEnduce,
);

Cross-stream grammar fusion

use dsfb_semiotics_calculus::{
    composition::GrammarFusion,
    AdmissibilityEnvelope, ResidualSign,
};

let env_a = AdmissibilityEnvelope::new(0.1, 1.0, 0.02);
let env_b = AdmissibilityEnvelope::new(0.2, 0.8, 0.04);
let mut fusion = GrammarFusion::new();

let stream_a = [0.3_f64, 0.6, 1.05];
let stream_b = [0.5_f64, 0.75, 0.6];

for (k, (&ra, &rb)) in stream_a.iter().zip(stream_b.iter()).enumerate() {
    let sa = ResidualSign::from_scalar(ra, 0.0, 0.0);
    let sb = ResidualSign::from_scalar(rb, 0.0, 0.0);
    let (ga, gb) = fusion.step(&sa, &env_a, &sb, &env_b);
    println!("k={k}: A={ga}  B={gb}  joint_vio={}", fusion.is_joint_violation());
}
// k=0: A=Adm  B=Adm  joint_vio=false
// k=1: A=Adm  B=Bdy  joint_vio=false
// k=2: A=Vio  B=Adm  joint_vio=true

Provenance replay

use dsfb_semiotics_calculus::{AdmissibilityEnvelope, HeuristicsBank, Observer};

let trajectory = vec![0.3, 0.6, 0.95, 1.1, 0.5_f64];

let mut obs1 = Observer::new(AdmissibilityEnvelope::new(0.1, 1.0, 0.02), HeuristicsBank::new());
let mut obs2 = Observer::new(AdmissibilityEnvelope::new(0.1, 1.0, 0.02), HeuristicsBank::new());

let episodes1 = obs1.observe_trajectory(&trajectory);
let episodes2 = obs2.observe_trajectory(&trajectory); // identical config → identical output

// ProvenanceTag is the replayable derivation certificate (Theorem 8.3)
for (e1, e2) in episodes1.iter().zip(episodes2.iter()) {
    assert_eq!(e1.provenance.step_range, e2.provenance.step_range);
    assert_eq!(e1.provenance.grammar_path, e2.provenance.grammar_path);
}

CLI artifact generator

# From the crate directory
cargo build --release
cargo run --release -- --output ./my-artifacts

Artifacts produced (13 total):

File Figure Key theorem
fig01_residual_sign_triple.svg Three-panel: ‖r(k)‖, ṙ(k), r̈(k) §2.1
fig02_admissibility_envelope.svg Concentric-circle envelope geometry Def 4.1
fig03_grammar_fsm_diagram.svg G = {Adm, Bdy, Vio} FSM + totality annotation Thm 3.1
fig04_grammar_state_trajectory.svg Residual trace colour-coded by grammar state Thm 3.1
fig05_persistence_counter.svg K(k) dwell-count bar chart §5.2
fig06_endoductive_operator.svg Data-flow: 4 inputs → ℰ → Episode(m, φ) Cor 5.4
fig07_provenance_tag_anatomy.svg ProvenanceTag field decomposition + replay Thm 8.3
fig08_bank_monotonicity.svg Bank growth: patterns + named motifs Thm 7.2
fig09_observer_noninterference.svg Host (mutable) vs Observer (pure) architecture Thm 3.2
fig10_cross_stream_fusion.svg G₁ ⋈ G₂ joint-violation panel Prop 7.1
summary.json Trajectory stats, envelope config, SC property list
report.md Full Markdown report with statistics table
dsfb-semiotics-calculus-artifacts.zip All 13 artifacts bundled

All figures are generated from a fixed 45-step synthetic trajectory embedded in src/figures.rs::synthetic_trajectory().


Full API reference

ResidualSign

pub struct ResidualSign {
    pub magnitude: f64,  // ‖r(k)‖ ≥ 0
    pub drift: f64,      // ṙ(k) = r(k) − r(k−1)
    pub slew: f64,       // r̈(k) = ṙ(k) − ṙ(k−1)
}
impl ResidualSign {
    pub fn from_scalar(r: f64, r_prev: f64, r_prev2: f64) -> Self
    pub fn is_drifting_outward(&self) -> bool  // drift > 0
}

AdmissibilityEnvelope / EnvelopeFamily

pub struct AdmissibilityEnvelope {
    pub rho_min: f64,  // inner exclusion radius ρ_min > 0
    pub rho_max: f64,  // outer boundary ρ_max ≥ ρ_min
    pub delta: f64,    // boundary half-width δ ∈ (0, ρ_min/4]
}
impl AdmissibilityEnvelope {
    pub fn new(rho_min: f64, rho_max: f64, delta: f64) -> Self  // panics if invariants violated
    pub fn classify(&self, magnitude: f64) -> EnvelopeRegion    // Interior / Boundary / Exterior
}

pub enum EnvelopeRegion { Interior, Boundary, Exterior }

pub struct EnvelopeFamily { /* sorted by ρ_max */ }
impl EnvelopeFamily {
    pub fn new(envelopes: Vec<AdmissibilityEnvelope>) -> Self  // enforces monotonicity
    pub fn get(&self, lambda: usize) -> Option<&AdmissibilityEnvelope>
    pub fn len(&self) -> usize
}

GrammarFsm / GrammarState

pub enum GrammarState { Admissible, Boundary, Violation }
impl GrammarState {
    pub fn is_non_nominal(&self) -> bool  // Bdy or Vio
    pub fn is_violation(&self) -> bool    // Vio only
}

pub struct GrammarFsm { /* current state + persistence counter */ }
impl GrammarFsm {
    pub fn new() -> Self                                                               // → Admissible
    pub fn state(&self) -> GrammarState
    pub fn persistence(&self) -> usize                                                 // K(k)
    pub fn step(&mut self, sign: &ResidualSign, env: &AdmissibilityEnvelope) -> GrammarState
    // total — no undefined path, no Option
}

Motif

pub enum Motif {
    Named(String),  // bank-matched structural pattern
    Unknown,        // no bank match; ProvenanceTag carries full descriptor
}
impl Motif {
    pub fn named(name: impl Into<String>) -> Self
    pub fn is_unknown(&self) -> bool
}

ProvenanceTag

pub struct ProvenanceTag {
    pub sign_sequence: Vec<ResidualSign>,   // observed sign evolution σ
    pub grammar_path: Vec<GrammarState>,    // grammar state sequence g
    pub add_descriptor: String,             // serialized ADD invariant α
    pub step_range: (usize, usize),         // [start_k, end_k]
}
impl ProvenanceTag {
    pub fn window_len(&self) -> usize          // end_k − start_k + 1
    pub fn contains_violation(&self) -> bool   // any Vio in grammar_path
}

Episode

pub struct Episode {
    pub motif: Motif,
    pub provenance: ProvenanceTag,
}
impl Episode {
    pub fn new(motif: Motif, provenance: ProvenanceTag) -> Self
    pub fn is_unknown(&self) -> bool
}
// Display: "Episode(SlowRamp, steps 5–12)"

Enduce trait / DefaultEnduce

pub trait Enduce {
    fn enduce(
        &self,
        signs: &[ResidualSign],
        grammar_path: &[GrammarState],
        bank: &HeuristicsBank,
        step_range: (usize, usize),
        add_descriptor: &str,
    ) -> Episode;  // total — never panics, never returns None
}

// Day-One implementation: always returns Unknown with full ProvenanceTag
pub struct DefaultEnduce;

HeuristicsBank / MotifPattern

pub struct MotifPattern {
    pub name: String,
    pub min_persistence: usize,   // minimum consecutive non-nominal steps
    pub requires_violation: bool, // must include confirmed Vio state
    pub min_drift: f64,           // minimum outward drift to trigger
}

pub struct HeuristicsBank { /* monotone HashMap */ }
impl HeuristicsBank {
    pub fn new() -> Self
    pub fn augment(&mut self, name: impl Into<String>, pattern: MotifPattern)
    // no remove() — bank is monotone by construction (Theorem 7.2)
    pub fn motif_count(&self) -> usize
    pub fn is_empty(&self) -> bool
    pub fn match_episode(
        &self,
        signs: &[ResidualSign],
        grammar_path: &[GrammarState],
        persistence: usize,
    ) -> Motif
}

Observer<E: Enduce>

pub struct Observer<E: Enduce = DefaultEnduce> { /* envelope, bank, enduce, fsm */ }

impl Observer<DefaultEnduce> {
    pub fn new(envelope: AdmissibilityEnvelope, bank: HeuristicsBank) -> Self
}
impl<E: Enduce> Observer<E> {
    pub fn with_enduce(envelope: AdmissibilityEnvelope, bank: HeuristicsBank, enduce: E) -> Self
    pub fn set_add_descriptor(&mut self, desc: impl Into<String>)
    pub fn observe_step(
        &mut self,
        magnitude: f64,
        prev: f64,
        prev2: f64,
        k: usize,
    ) -> Option<Episode>
    pub fn observe_trajectory(&mut self, residuals: &[f64]) -> Vec<Episode>
}

GrammarFusion

pub struct GrammarFusion { /* fsm1, fsm2 */ }
impl GrammarFusion {
    pub fn new() -> Self
    pub fn step(
        &mut self,
        sign1: &ResidualSign, env1: &AdmissibilityEnvelope,
        sign2: &ResidualSign, env2: &AdmissibilityEnvelope,
    ) -> (GrammarState, GrammarState)
    pub fn is_joint_violation(&self) -> bool
}

Canonical figures

All 10 figures are generated identically by both the Rust CLI and the Colab notebook.

# Figure Theorem
1 Residual sign triple (‖r‖, ṙ, r̈) per step §2.1
2 Admissibility envelope geometry Def 4.1
3 Grammar FSM with totality annotation Thm 3.1
4 Grammar state sequence on residual trace Thm 3.1
5 Persistence counter K(k) per step §5.2
6 Endoductive operator data-flow Cor 5.4
7 ProvenanceTag anatomy + replay guarantee Thm 8.3
8 Bank monotonicity (augmentation steps) Thm 7.2
9 Observer non-interference architecture Thm 3.2
10 Cross-stream grammar fusion G₁ ⋈ G₂ Prop 7.1

Colour convention (both Rust and Python):

Grammar state Colour Hex
Admissible Green #1a7a4a
Boundary Amber #c47c00
Violation Red #c0392b

Colab notebook

colab/dsfb_semiotics_calculus_figures.ipynb reproduces all 10 figures using NumPy + Matplotlib on the identical 45-step synthetic trajectory.

Open In Colab

The notebook:

  • Installs fpdf2 for PDF generation
  • Runs all 10 figure cells (each cell is self-contained)
  • Writes PNGs + SVGs + PDF report + summary.json
  • Zips all artifacts and triggers one-click download from Colab

Benchmark excerpt

Theorem 8.3 — Provenance replay determinism (empirical verification):

Trajectory length:  45 steps
Episodes produced:  14 (varies with envelope params)
Replay match rate:  100/100 trials (identical Episode bit-for-bit)
Max |Δ| on replay:  0.0  (deterministic floating-point; same trajectory, same hardware)

Replay is deterministic because ℰ is a pure function of (trajectory slice, bank). The ProvenanceTag captures exactly the inputs needed to reproduce the output.


Citation

de Beer, R. (2026). DSFB Structural Semiotics Calculus Endoduction as a Fourth Mode of Inference: Formal Syntax, Composition Rules, and Provable Properties of Endoductive Inference over Residual Trajectories (v1.0). Zenodo. https://doi.org/10.5281/zenodo.19446580

@misc{deBeer2026dssc,
  author    = {de Beer, Riaan},
  title     = {{DSFB} Structural Semiotics Calculus --- Endoduction as a Fourth Mode of
               Inference: Formal Syntax, Composition Rules, and Provable Properties of
               Endoductive Inference over Residual Trajectories},
  year      = {2026},
  month     = {April},
  version   = {v1.0},
  publisher = {Zenodo},
  doi       = {10.5281/zenodo.19446580},
  url       = {https://doi.org/10.5281/zenodo.19446580}
}

IP notice

Apache 2.0 applies to this software artifact as an executable and distributable work. It does not constitute a license to the underlying theoretical framework, mathematical architecture, formal constructions, or supervisory methods described in the companion paper, which constitute proprietary Background IP of Invariant Forge LLC (Delaware LLC No. 10529072).

Commercial deployment of the DSSC framework or any derivative thereof requires a separate written license agreement.

Contact: licensing@invariantforge.net