Skip to content

AlgebraicEpiMech API

Reference for the exported API of AlgebraicEpiMech, generated from docstrings.

AlgebraicEpiMech.AtCompartment — Type.

struct AtCompartment <: ObservationTarget
AtCompartment(species)

Sample a SPECIES: prevalence. Every species whose (flattened) name begins with species gains a catalytic tap X -> X + O, carrying its own detection rate — whoever is in the compartment is currently detectable (although detection does not cause removal)


Fields

  • species::Symbol

AlgebraicEpiMech.AtEvent — Type.

struct AtEvent <: ObservationTarget
AtEvent(transition)

Record a TRANSITION: incidence. Every transition whose (flattened) name begins with transition gains an output arc into an accumulator, so it fires at that transition's own rate and counts exactly one observation per occurrence.

AtEvent(:transmission) counts infections as they happen, which is what decouples a reporting delay from the latent period — the delay is then whatever the chain adds and nothing else.


Fields

  • transition::Symbol

AlgebraicEpiMech.CompartmentalModel — Type.

abstract type CompartmentalModel <: EpiMechModel

Abstract base type for compartmental epidemiological models.

Compartmental models define the specific disease dynamics and transitions between epidemiological states (e.g., S→I→R) as undirected wiring diagrams.


Fields

AlgebraicEpiMech.CompleteCrossImmunity — Type.

struct CompleteCrossImmunity <: MultiStrainModel

Complete cross-immunity multistrain model.

In the complete cross-immunity model:

  • All strains share a common susceptible pool
  • Infection by any strain confers immunity to all strains
  • Typing: UninfectedInfectedTyping (typed S vs I/R compartments)
  • Composition: Strains compete for susceptibles via shared S depletion

Fields

  • number_of_strains::Int: Number of competing strains in the model
  • strain_names::Vector{Symbol}: Names for each strain (e.g., [:h1n1, :h3n2])

Examples

# Create a 2-strain model with custom names
multistrain = CompleteCrossImmunity([:wild_type, :variant])

# Create a 3-strain model with auto-generated names
multistrain = CompleteCrossImmunity(3)  # Creates [:strain_1, :strain_2, :strain_3]

# Compose to create competing strain SIR
typing = UninfectedInfectedTyping()
strain_typed = create_model(typing, multistrain)
sir_typed = create_model(typing, SIR())
combined = typed_product(sir_typed, strain_typed)

Fields

  • number_of_strains::Int64
  • strain_names::Vector{Symbol}

AlgebraicEpiMech.ContactStratification — Type.

struct ContactStratification <: Stratification

Contact-based stratification for epidemiological models.

Represents stratifications where populations are divided into strata with contact-based interactions (full contact matrix between all pairs). This generalizes age groups, geographic regions modelled as cross-region contact (rather than explicit movement), risk groups, etc.

Multiple contact stratifications can be stacked via compose_stratifications to create product strata (e.g., age × geography).

Fields

  • stratum_names::Vector{Symbol}: Names of strata (e.g., [:child, :adult] or [:urban, :rural])
  • label::Symbol: Semantic label for the stratification (e.g., :age, :geography, :risk)

Reflexive boxes (disease, reversion, and waning) are controlled via the include_reflexives keyword on create_model, not on the struct itself.

Examples

# Age stratification
age = ContactStratification([:child, :adult], :age)

# Geographic stratification
geo = ContactStratification([:urban, :rural], :geography)

# Compose to create age × geography strata
typing = OnePopulationTyping()
sir = create_model(typing, SIR())
age_sir = typed_product(sir, create_model(typing, age))
age_geo_sir = typed_product(age_sir, create_model(typing, geo))
# Result: 4 strata (childxurban, childxrural, adultxurban, adultxrural)

See also: AgeStratification, GeographicStratification


Fields

  • stratum_names::Vector{Symbol}
  • label::Symbol

AlgebraicEpiMech.EpiMechModel — Type.

abstract type EpiMechModel

Abstract base type for all epidemiological-mechanical models in the AlgebraicEpiMech framework.

All concrete epidemiological-mechanical model types should be subtypes of EpiMechModel. This abstract type serves as the root of the type hierarchy for models that combine epidemiological dynamics with mechanical or algebraic structures.

Extended help

Subtypes of EpiMechModel should implement the necessary interface methods for their specific model formulation.

See also

  • Related concrete model types (define as needed)

Fields

AlgebraicEpiMech.EpidemiologicalTyping — Type.

abstract type EpidemiologicalTyping

Abstract base type for epidemiological typing strategies.

An EpidemiologicalTyping describes how populations and transitions are typed when constructing epidemiological models. type_system materializes the strategy as the LabelledPetriNet used as the codomain of a typed Petri net.

Reference

Libkind et al. (2023) An algebraic framework for structured epidemic modelling


Fields

AlgebraicEpiMech.FullHistory — Type.

struct FullHistory <: ImmuneHistoryMode

Full (unordered) immune history: uninfected individuals are indexed by the set of strains they are immune to (2^n classes). Immunity accumulates — reversion routes to h ∪ {i}.


Fields

AlgebraicEpiMech.ImmuneHistory — Type.

struct ImmuneHistory{M<:ImmuneHistoryMode} <: MultiStrainModel
ImmuneHistory{M<:ImmuneHistoryMode} <: MultiStrainModel

Immune-history combination of the available strain/subtype names and ImmuneHistoryMode.

Fields

  • strain_names::Vector{Symbol}: strain names (e.g. [:current, :invader])
  • mode::M: FullHistory() or LatestInfection()

Examples

typing = UninfectedInfectedTyping()

seirs   = create_model(typing, SEIRS())
history = create_model(typing, ImmuneHistory([:current, :invader]))

model = typed_product(seirs, history)   # immune-history-resolved SEIRS
# States: S_naive, S_current, …, E_invader_from_current, I_…, R_…

Fields

  • strain_names::Vector{Symbol}
  • mode::ImmuneHistoryMode

AlgebraicEpiMech.ImmuneHistory — Method.

ImmuneHistory(n::Int; kwargs...)

Auto-name n strains as strain_1 … strain_n.

AlgebraicEpiMech.ImmuneHistoryMode — Type.

abstract type ImmuneHistoryMode

Abstract base type for selections of how much immune history is retained.


Fields

AlgebraicEpiMech.LatestInfection — Type.

struct LatestInfection <: ImmuneHistoryMode

Latest-infection status: uninfected individuals are indexed by their most recent infection (n + 1 classes). Reversion overwrites immune status — routes to {i}.


Fields

AlgebraicEpiMech.MultiStrainModel — Type.

abstract type MultiStrainModel <: EpiMechModel

Abstract base type for multistrain epidemiological models.

Multistrain models define how multiple pathogen strains interact through different assumptions about cross-immunity. These compose with compartmental models via typed_product to create strain-structured disease dynamics.


Fields

AlgebraicEpiMech.NoCrossImmunity — Type.

struct NoCrossImmunity <: MultiStrainModel

No cross-immunity multistrain model.

In the no cross-immunity model:

  • Each strain operates independently with no interaction
  • Strains act as independent strata (similar to age groups)
  • Typing: OnePopulationTyping (all compartments have the same type)
  • Composition: Creates parallel disease dynamics per strain via typed_product

Fields

  • number_of_strains::Int: Number of independent strains in the model
  • strain_names::Vector{Symbol}: Names for each strain (e.g., [:h1n1, :h3n2, :b])

Examples

# Create a 3-strain model with custom names
multistrain = NoCrossImmunity([:h1n1, :h3n2, :b])

# Create a 2-strain model with auto-generated names
multistrain = NoCrossImmunity(2)  # Creates [:strain_1, :strain_2]

# Compose to create strain-structured SIR
typing = OnePopulationTyping()
strain_typed = create_model(typing, multistrain)
sir_typed = create_model(typing, SIR())
combined = typed_product(sir_typed, strain_typed)

Fields

  • number_of_strains::Int64
  • strain_names::Vector{Symbol}

AlgebraicEpiMech.ObservationChainLayout — Type.

struct ObservationChainLayout{N}
ObservationChainLayout(source_name, obs_names)

Metadata for one observation chain.

Fields

  • source_name: Flattened name of the event or compartment being observed
  • obs_names: Observation-state names in stage order
  • cumulative_name: Terminal observation-state name, derived from obs_names

Fields

  • source_name::Symbol
  • obs_names::NTuple{N, Symbol} where N
  • cumulative_name::Symbol

AlgebraicEpiMech.ObservationLayout — Type.

struct ObservationLayout{N, C<:Tuple, M}
ObservationLayout(obs_names, chains)

Metadata for all observation chains in an augmented Petri net.

Fields

  • obs_names: All flattened observation-state names in Petri-net order
  • chains: Observation chains in first-encounter order
  • cumulative_names: Terminal state of each chain, derived from chains

Fields

  • obs_names::NTuple{N, Symbol} where N
  • chains::Tuple
  • cumulative_names::NTuple{M, Symbol} where M

AlgebraicEpiMech.ObservationTarget — Type.

abstract type ObservationTarget
ObservationTarget

What an observation attaches to. AtEvent records a transition (incidence); AtCompartment samples a species (prevalence).


Fields

AlgebraicEpiMech.OnePopulationTyping — Type.

struct OnePopulationTyping <: EpidemiologicalTyping
OnePopulationTyping(; population_type = :Population)

Typing strategy in which every compartment belongs to one population type and therefore has the same available stratifications.

Examples

typing = OnePopulationTyping()
typing = OnePopulationTyping(population_type = :CityPopulation)

Fields

  • population_type::Symbol: The symbolic name for the population type.

AlgebraicEpiMech.SEI — Type.

struct SEI <: CompartmentalModel

Susceptible-Exposed-Infected (SEI) compartmental model.

The SEI model extends SI with an exposed compartment:

  • Susceptible (S): Individuals who can become infected
  • Exposed (E): Individuals who are infected but not yet infectious
  • Infected (I): Individuals who are infectious

Transitions:

  • S + I → E + I (exposure/transmission)
  • E → I (progression to infectious)

The SEI model represents diseases with an incubation period but no recovery.


Fields

  • number_E_stages::Int64
  • number_I_stages::Int64
  • number_of_states::Int64

AlgebraicEpiMech.SEIR — Type.

struct SEIR <: CompartmentalModel

Susceptible-Exposed-Infected-Recovered (SEIR) compartmental model.

The SEIR model extends SEI with recovery:

  • Susceptible (S): Individuals who can become infected
  • Exposed (E): Individuals who are infected but not yet infectious
  • Infected (I): Individuals who are infectious
  • Recovered (R): Individuals who have recovered and gained immunity

Transitions:

  • S + I → E + I (exposure/transmission) [from SEI]
  • E → I (progression to infectious) [from SEI]
  • I → R (recovery) [extension]

Built by extending SEI model with recovery transition.


Fields

  • number_E_stages::Int64
  • number_I_stages::Int64
  • number_of_states::Int64

AlgebraicEpiMech.SEIRS — Type.

struct SEIRS <: CompartmentalModel

Susceptible-Exposed-Infected-Recovered-Susceptible (SEIRS) compartmental model.

The SEIRS model extends SEIR with waning immunity:

  • Susceptible (S): Individuals who can become infected
  • Exposed (E): Individuals who are infected but not yet infectious
  • Infected (I): Individuals who are infectious
  • Recovered (R): Individuals who have recovered but may lose immunity

Transitions:

  • S + I → E + I (exposure/transmission) [from SEIR]
  • E → I (progression to infectious) [from SEIR]
  • I → R (recovery) [from SEIR]
  • R → S (waning immunity) [extension]

Built by extending SEIR model with waning transition.


Fields

  • number_E_stages::Int64
  • number_I_stages::Int64
  • number_of_states::Int64

AlgebraicEpiMech.SEIS — Type.

struct SEIS <: CompartmentalModel

Susceptible-Exposed-Infected-Susceptible (SEIS) compartmental model.

The SEIS model extends SEI with waning immunity (reversion):

  • Susceptible (S): Individuals who can become infected
  • Exposed (E): Individuals who are infected but not yet infectious
  • Infected (I): Individuals who are infectious

Transitions:

  • S + I → E + I (exposure/transmission) [from SEI]
  • E → I (progression to infectious) [from SEI]
  • I → S (waning immunity/reversion) [extension]

The SEIS model represents diseases with incubation period but no lasting immunity.


Fields

  • number_E_stages::Int64
  • number_I_stages::Int64
  • number_of_states::Int64

AlgebraicEpiMech.SI — Type.

struct SI <: CompartmentalModel

Susceptible-Infected (SI) compartmental model.

The SI model includes:

  • Susceptible (S): Individuals who can become infected
  • Infected (I): Individuals who are infectious

Transitions:

  • S + I → I + I (infection/transmission)

The SI model represents endemic diseases with no recovery.


Fields

  • number_I_stages::Int64
  • number_of_states::Int64

AlgebraicEpiMech.SIR — Type.

struct SIR <: CompartmentalModel

Susceptible-Infected-Recovered (SIR) compartmental model.

The SIR model extends SI with recovery:

  • Susceptible (S): Individuals who can become infected
  • Infected (I): Individuals who are infectious
  • Recovered (R): Individuals who have recovered and gained immunity

Transitions:

  • S + I → I + I (infection/transmission) [from SI]
  • I → R (recovery) [extension]

Built by extending SI model with recovery transition.


Fields

  • number_I_stages::Int64
  • number_of_states::Int64

AlgebraicEpiMech.SIS — Type.

struct SIS <: CompartmentalModel

Susceptible-Infected-Susceptible (SIS) compartmental model.

The SIS model extends SI with waning immunity (reversion):

  • Susceptible (S): Individuals who can become infected
  • Infected (I): Individuals who are infectious

Transitions:

  • S + I → I + I (infection/transmission) [from SI]
  • I → S (waning immunity/reversion) [extension]

The SIS model represents diseases with no lasting immunity.


Fields

  • number_I_stages::Int64
  • number_of_states::Int64

AlgebraicEpiMech.Stratification — Type.

abstract type Stratification <: EpiMechModel

Represents a stratification structure for epidemiological models.


Fields

AlgebraicEpiMech.UninfectedInfectedTyping — Type.

struct UninfectedInfectedTyping <: EpidemiologicalTyping
UninfectedInfectedTyping(; uninfected_type = :Uninfected, infected_type = :Infected)

Typing strategy that distinguishes uninfected and infected populations. The two populations may have different stratifications.

Examples

typing = UninfectedInfectedTyping()
typing = UninfectedInfectedTyping(
    uninfected_type = :Susceptible,
    infected_type = :Infectious,
)

See also: type_system, OnePopulationTyping


Fields

  • uninfected_type::Symbol: The symbolic name for the uninfected population.
  • infected_type::Symbol: The symbolic name for the infected population.

AlgebraicEpiMech.AgeStratification — Method.

AgeStratification(
    names::Vector{Symbol}
) -> ContactStratification
AgeStratification(age_group_names::Vector{Symbol})
AgeStratification(names...)

Convenience constructor for age-based contact stratification.

Creates a ContactStratification with label :age. Provided for backward compatibility and semantic clarity.

Examples

age = AgeStratification([:child, :adult, :elderly])
age = AgeStratification(:child, :adult, :elderly)  # splatting syntax

AlgebraicEpiMech.GeographicStratification — Method.

GeographicStratification(
    names::Vector{Symbol}
) -> ContactStratification
GeographicStratification(location_names::Vector{Symbol})
GeographicStratification(names...)

Convenience constructor for geography-based contact stratification.

Creates a ContactStratification with label :geography. Represents geographic regions with cross-region contact (not movement-based models).

Examples

geo = GeographicStratification([:urban, :suburban, :rural])
geo = GeographicStratification(:urban, :suburban, :rural)  # splatting syntax

AlgebraicEpiMech.add_disease_progression! — Method.

add_disease_progression!(
    uwd,
    from_junction,
    to_junction,
    typing::OnePopulationTyping
) -> Any

Add disease progression mechanism: fromcompartment → tocompartment

Used for transitions like E→I, I→R, etc. (infected → infected transitions)

AlgebraicEpiMech.add_infection! — Method.

add_infection!(
    uwd,
    infectee_junction,
    infector_junction,
    first_infected_junction,
    typing::OnePopulationTyping
) -> Any

Add infection mechanism: Infectee + Infector → first_infected + Infector

General infection mechanism where an infectee (typically S) interacts with an infector (typically I) to produce a newly infected individual that enters the first*infected compartment, while the infector remains unchanged. The first*infected_junction can be:

  • An I stage for SIR-like models (direct infection)
  • An E stage for SEIR-like models (exposure before infectiousness)

The typing argument determines the population types used.

AlgebraicEpiMech.add_reversion_progression! — Method.

add_reversion_progression!(
    uwd,
    from_junction,
    to_junction,
    typing::OnePopulationTyping
) -> Any

Add reversion progression mechanism: e.g. R → S (infected → uninfected transitions)

Used for transitions from infected compartments back to uninfected compartments.

AlgebraicEpiMech.add_uninfected_density_progression! — Method.

add_uninfected_density_progression!(
    uwd,
    from_junction,
    to_junction,
    typing::OnePopulationTyping
) -> Any

Add uninfected density progression mechanisms. This is useful for transitions like waning partial immunity (uninfected → uninfected transitions, e.g., V → S).

AlgebraicEpiMech.attach_observation — Method.

attach_observation(
    pn,
    target::ObservationTarget;
    n_stages,
    prefix
) -> Any
attach_observation(pn, target; n_stages = 1, prefix = :O) -> LabelledPetriNet

Attach an observation chain to pn, returning a new net. The result is the colimit described at the top of this file, materialized in one direct pass (see the note there for why it is not built by pushout).

n_stages Erlang delay stages are added per chain: the first receives the observation, each subsequent one takes flow from the last, and the final stage is cumulative (no outflow), i.e. the accumulator the observation model reads.

Reapplying the same target, prefix, and stage count is idempotent. Reusing a target and prefix for an incompatible chain shape throws an ArgumentError; choose a different prefix when both observation layouts are required.

Chains are grouped by stratum. For AtEvent the group is the transition's net product — for infection routes S_x + I_y -> E_x + I_y that is E_x, so all routes infecting x count into x's chain. For AtCompartment each matched species gets its own chain.

Apply this after all typed_product composition so chains are grouped over the final strata.

AlgebraicEpiMech.compose_stratifications — Method.

compose_stratifications(
    a::ContactStratification,
    b::ContactStratification
) -> ContactStratification

Compose two ContactStratifications via Cartesian product.

Arguments

  • a::ContactStratification: First contact stratification
  • b::ContactStratification: Second contact stratification

Returns

  • ContactStratification: New contact stratification representing the product of a and b, with combined stratum names and a composite label.

Examples

age = ContactStratification([:child, :adult], :age)
geo = ContactStratification([:urban, :rural], :geography)
age_geo = compose_stratifications(age, geo)
# Resulting stratum names: [:childxurban, :childxrural, :adultxurban, :adultxrural]
# Resulting label: :age_x_geography

AlgebraicEpiMech.create_model — Method.

create_model(
    typing::EpidemiologicalTyping,
    model::EpiMechModel;
    population_transitions,
    include_reflexives
) -> Catlab.CategoricalAlgebra.Pointwise.ACSetTransformations.StructACSetTransformation{ACSets.Schemas.TypeLevelBasicSchema{Symbol, Tuple{:T, :S, :I, :O}, Tuple{(:it, :I, :T), (:is, :I, :S), (:ot, :O, :T), (:os, :O, :S)}, Tuple{:Name}, Tuple{(:tname, :T, :Name), (:sname, :S, :Name)}, Tuple{}}, Comp, AlgebraicPetri.LabelledPetriNet, AlgebraicPetri.LabelledPetriNet} where Comp<:NamedTuple

Create a typed Petri net model from an epidemiological typing strategy and model specification.

This is the unified entry point for creating all model types in AlgebraicEpiMech. Through multiple dispatch on the model parameter, it supports any subtype of EpiMechModel. The undirected wiring diagram (UWD) provides an intermediate structural representation, recording how compartments and mechanisms connect independently of the final typed Petri net constructed by oapply_typed.

Functionality Overview

The create_model function builds the typed Petri net representation of the epidemiological-mechanical model in four steps:

  1. Build Type System: Materializes the typing strategy as a LabelledPetriNet
  2. Create UWD: Builds an undirected wiring diagram with model structure and box names
  3. Generate Transition Names: Creates unique names for ODE parameters via dispatch
  4. Apply Typing: Uses oapply_typed to create a typed Petri net

The resulting typed Petri net can be used directly for ODE simulation or composed with other typed Petri nets via typed_product to build complex hierarchical models.

Arguments

  • typing::EpidemiologicalTyping: The strategy defining possible compartment and transition types.
  • model::EpiMechModel: The model specification (uses dispatch for different types)

Keyword Arguments

  • population_transitions=nothing: Extra transition types for type_system, as a single pair such as :strata => (:Population => :Population) or a collection of pairs. nothing gives the standard type system. Models that will be composed with typed_product must be created with the same extra transitions so that they share a codomain.
  • include_reflexives::Bool=true (for ContactStratification, forwarded to create_model_uwd): Controls whether per-stratum reflexive boxes (disease, reversion, and waning) are added. Required for typed_product composition; disable for standalone use to avoid zero-effect transitions.

Returns

  • ACSetTransformation: A typed Petri net with two components accessible via:

    • dom(typed_model): Extract the underlying LabelledPetriNet for ODE solving
    • codom(typed_model): Access the Petri-net type system

Model-Specific Transition Naming

Transition names are generated automatically based on model type:

  • Compartmental: transmission_S_I, E_to_I, I_to_R (describes flow between compartments)
  • Age Stratification: child_child, child_adult, adult_child (infectee_infector pattern)
  • Multistrain: Strain identifiers like h1n1, h3n2 (enables clean composition)

These names become ODE parameters and differ from box names (:transmission, :density) used for composition.

Examples

# Basic compartmental model
typing = OnePopulationTyping()
seir = create_model(typing, SEIR())
seir_pn = dom(seir)  # Extract for ODE solving
# Transitions: :transmission_S_I, :E_to_I, :I_to_R

# Multi-stage compartments (gamma-distributed delays)
si_multi = create_model(typing, SI(number_I_stages=3))
# Transitions: :transmission_S_I1, :transmission_S_I2, :transmission_S_I3, :I1_to_I2, :I2_to_I3

# Age-structured model
age_strat = AgeStratification([:child, :adult, :elderly])
age_model = create_model(typing, age_strat)
# Transitions: :child_child, :child_adult, :child_elderly, :adult_child, ...

# Multistrain model (no cross-immunity)
strains = NoCrossImmunity([:h1n1, :h3n2, :seasonal_b])
strain_model = create_model(typing, strains)

# Compose models via typed_product
sir = create_model(typing, SIR())
age = create_model(typing, AgeStratification([:child, :adult]))
age_sir = typed_product(sir, age)
age_sir_pn = dom(age_sir)
# Result: S_child, I_child, R_child, S_adult, I_adult, R_adult compartments
# Transition names become tuples: (:transmission_S_I, :child_adult)

Compositional Design

Models expressed as typed Petri nets compose via typed_product using their common type-system codomain when box names match. The vectorfield_flat function automatically handles tuple transition names in composed models for ODE parameter assignment.

See also: create_model_uwd, generate_transition_names, type_system, typed_product (from AlgebraicPetri.TypedPetri), vectorfield_flat

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::CompartmentalModel
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Create an undirected wiring diagram (UWD) for a compartmental model within a specific typing.

This function returns the undirected wiring diagram that defines the compartmental structure and transitions, using the type names from the typing. The compartments are typed according to the typing's population structure. The returned UWD does not declare an outer boundary/ports; oapply_typed derives the boundary from its junctions when materializing the typed Petri net.

Arguments

  • typing::EpidemiologicalTyping: The population typing defining type names
  • model::CompartmentalModel: The compartmental model instance (SI(), SEI(), SIR(), SEIR(), etc.)

Returns

  • Undirected wiring diagram defining the compartmental model structure with typing-specific types

Examples

# Create SIR model UWD with single population typing
typing = OnePopulationTyping(population_type = :Individual)
sir_uwd = create_model_uwd(typing, SIR())  # Uses Individual type

# Create SEIR model UWD with uninfected/infected typing
typing = UninfectedInfectedTyping(uninfected_type = :Susceptible, infected_type = :Infectious)
seir_uwd = create_model_uwd(typing, SEIR())  # S::Susceptible, E,I,R::Infectious

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::ContactStratification;
    include_reflexives
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct an undirected wiring diagram (UWD) for contact strata.

Creates an UWD where transmission boxes represent contact patterns between different strata. Example strata include age groups, risk levels, or demographic divisions, but only covers instantaneous transmission dynamics, e.g. movement between geographic regions would require additional demographic modeling.

The UWD structure depends on the typing:

  • OnePopulationTyping: Single junction per age group (combined susceptible/infected)
  • UninfectedInfectedTyping: Two junctions per age group (uninfected and infected)

All transmission boxes are created with the :transmission name, enabling composition with compartmental models that have :transmission boxes (SI, SIR, SEIR, etc.).

Arguments

  • typing::EpidemiologicalTyping: The population typing defining type system
  • model::ContactStratification: The contact stratification configuration with stratum names

Returns

  • Undirected wiring diagram with stratum junctions and transmission boxes for composition

Mathematical Structure

For n strata, creates n² transmission boxes representing the contact matrix:

  • Diagonal boxes (i→i): within-stratum transmission
  • Off-diagonal boxes (i→j, i≠j): between-stratum transmission

Composition Behavior

When composed with a compartmental model UWD via typed_product, the transmission boxes will align based on the :transmission name, allowing the contact structure to modulate the disease dynamics defined in the compartmental model.

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::SEIRS
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct a complete SEIRS model UWD by extending SEI with recovery and waning immunity.

Builds on the SEI base model (S→E→I dynamics with multi-stage E and I support) and adds:

  • R (recovered) compartment
  • Recovery transition: I_last → R
  • Waning immunity transition: R → S

Represents diseases with latent periods and temporary immunity. The dual multi-stage capability enables independent control of latent and infectious period distributions. Recovery occurs from the last I stage, and waning immunity returns individuals from R to S.

Usage with oapply_typed

Apply to typing with oapply_typed to create a typed Petri net for composition.

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::SEIR
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct a complete SEIR model UWD by extending SEI with recovery.

Builds on the SEI base model (S→E→I dynamics with multi-stage E and I support) and adds:

  • R (recovered) compartment
  • Recovery transition: I_last → R

The dual multi-stage capability (independent E and I stages) enables independent control of latent and infectious period distributions. Recovery always occurs from the last I stage.

Usage with oapply_typed

Apply to typing with oapply_typed to create a typed Petri net for composition.

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::SEIS
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct a complete SEIS model UWD by extending SEI with waning immunity.

Builds on the SEI base model (S→E→I dynamics with multi-stage E and I support) and adds:

  • Reversion transition: I_last → S (waning immunity)

Represents diseases with latent periods but no lasting immunity. The dual multi-stage capability enables independent control of latent and infectious period distributions. Reversion occurs from the last I stage back to S.

Usage with oapply_typed

Apply to typing with oapply_typed to create a typed Petri net for composition.

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::SIR
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct a complete SIR model UWD by extending SI with recovery.

Builds on the SI base model (S→I dynamics with multi-stage I support) and adds:

  • R (recovered) compartment
  • Recovery transition: I_last → R

The multi-stage I capability from SI is preserved, enabling gamma-distributed infectious periods. Recovery always occurs from the last I stage, ensuring proper sequencing.

Usage with oapply_typed

Apply to typing with oapply_typed to create a typed Petri net for composition.

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::SIS
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct a complete SIS model UWD by extending SI with waning immunity.

Builds on the SI base model (S→I dynamics with multi-stage I support) and adds:

  • Reversion transition: I_last → S (waning immunity)

Represents diseases where recovery returns individuals to susceptible state. The multi-stage I capability enables realistic infectious period distributions. Reversion occurs from the last I stage back to S.

Usage with oapply_typed

Apply to typing with oapply_typed to create a typed Petri net for composition.

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::EpidemiologicalTyping,
    model::Union{SEI, SI}
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct a complete undirected wiring diagram (UWD) for SI or SEI models.

Populates an initially empty UWD with compartments and mechanisms via setup_basic!. These are the base models that other compartmental models extend from.

Usage with oapply_typed

The returned UWD can be applied to the typing using oapply_typed to create a typed Petri net, enabling composition with other typed Petri nets (e.g., demographic processes, interventions).

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(_::OnePopulationTyping, _::ImmuneHistory)

Immune-history stratification requires the uninfected/infected type split.

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::OnePopulationTyping,
    multistrain::NoCrossImmunity
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct an undirected wiring diagram (UWD) for a no cross-immunity multistrain model.

Creates a strain-stratified UWD where each strain operates independently. The strains are represented as separate junctions, and each strain has its own set of disease transition boxes (:transmission, :disease, :reversion) that will compose with corresponding boxes from a compartmental model via typed_product.

All box types are always included; typed_product will only compose boxes that exist in both UWDs, naturally filtering out non-matching transitions. That filtering is silent — a transition present in one factor and absent from the other is dropped without error — which is why each factor carries a reflexive box per transition type it wants preserved.

There is deliberately no reflexive observation box. Observation is attached to the COMPOSED net by attach_observation, so a stratification factor never has to know it exists.

Arguments

  • typing::OnePopulationTyping: The required single-population typing
  • multistrain::NoCrossImmunity: The multistrain model configuration with strain names

Returns

  • Undirected wiring diagram with strain junctions and transition boxes for composition

Examples

# Create a 3-strain model with custom names
typing = OnePopulationTyping()
multistrain = NoCrossImmunity([:h1n1, :h3n2, :b])
strain_uwd = create_model_uwd(typing, multistrain)

# Compose with compartmental model
sir_typed = create_model(typing, SIR())
strain_typed = create_model(typing, multistrain)
combined = typed_product(sir_typed, strain_typed)

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::UninfectedInfectedTyping,
    multistrain::CompleteCrossImmunity
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct an undirected wiring diagram (UWD) for a complete cross-immunity multistrain model.

Creates a strain-structured UWD where all strains share a common susceptible pool but have separate infected compartments. Infection by any strain depletes the shared susceptible pool and confers immunity to all strains.

The UWD has:

  • One shared uninfected/susceptible junction
  • N infected strain junctions
  • Transmission boxes connecting shared susceptible to each strain's infected
  • Disease progression boxes for each strain's infected compartments
  • Reversion boxes to return to shared susceptible pool

Arguments

  • typing::UninfectedInfectedTyping: The required uninfected/infected typing
  • multistrain::CompleteCrossImmunity: The multistrain model configuration with strain names

Returns

  • Undirected wiring diagram with shared susceptible and strain-specific infected junctions

Examples

# Create a 2-strain competing model
typing = UninfectedInfectedTyping()
multistrain = CompleteCrossImmunity([:wild_type, :variant])
strain_uwd = create_model_uwd(typing, multistrain)

# Compose with compartmental model
sir_typed = create_model(typing, SIR())
strain_typed = create_model(typing, multistrain)
combined = typed_product(sir_typed, strain_typed)

AlgebraicEpiMech.create_model_uwd — Method.

create_model_uwd(
    typing::UninfectedInfectedTyping,
    model::ImmuneHistory
) -> Catlab.WiringDiagrams.RelationDiagrams.TypedUnnamedRelationDiagram{Symbol, Symbol, Symbol}

Construct the UWD for the ImmuneHistory stratification factor over a UninfectedInfectedTyping.

This is a stratification factor (like ContactStratification), meant to be typed_product-composed with a disease model — the disease model supplies S→E→I→R, while this factor supplies the immune-status structure.

  • Uninfected junctions U_h, one per immune-history class.
  • Infected junctions (h,i) = "history h, currently fighting strain i", one per (class, susceptible-strain) pair.
  • :transmission (escape-selective): U_h + (h',i) → (h,i) for every susceptible class h (i ∉ h) and every infector (h',i). A class immune to i simply has no such box, so the pullback drops that infection — the escape.
  • :disease is reflexive on each (h,i), so the base model's progression runs inside a fixed (h,i).
  • :reversion is off-diagonal: (h,i) → U_{recover} with recover = h∪{i} (FullHistory) or {i} (LatestInfection). This is the one stratum-changing move — recovering from i folds it into the immune history.
  • :waning reflexive on each U_h (inert with SEIRS; lets a waning-bearing base compose).

Examples

typing = UninfectedInfectedTyping()
history = create_model(typing, ImmuneHistory([:current, :invader]))
model = typed_product(create_model(typing, SEIRS()), history)

AlgebraicEpiMech.flatten_symbols — Method.

flatten_symbols(name::Tuple) -> Symbol

Recursively flatten a tuple of symbols and join them with underscores.

Examples

flatten_symbols((:x, :y, :z)) # returns :x_y_z
flatten_symbols(((:x, :y), :z)) # returns :x_y_z
flatten_symbols((((:a, :b), :c), :d)) # returns :a_b_c_d

AlgebraicEpiMech.generate_transition_names — Method.

generate_transition_names(
    uwd,
    model::CompartmentalModel
) -> Vector{Symbol}

Generate transition names from UWD structure for compartmental models.

For boxes with 2 ports (one input, one output), generates names in the format input_to_output. For boxes with 4 ports (two inputs, two outputs), generates names in the format box_name_input1_input2 where inputs are sorted in reverse alphabetical order for consistency (e.g., transmission_S_I).

Arguments

  • uwd: The undirected wiring diagram with species names in junction :variable attributes
  • model::CompartmentalModel: The compartmental model (used for dispatch)

Returns

  • Vector{Symbol}: Vector of unique transition names, one per box in the UWD

Examples

# Used internally by create_model
typing = OnePopulationTyping()
uwd = create_model_uwd(typing, SEIR())
names = generate_transition_names(uwd, SEIR())
# Returns: [:transmission_S_I, :E_to_I, :I_to_R]

See also: create_model

AlgebraicEpiMech.generate_transition_names — Method.

generate_transition_names(
    uwd,
    model::ContactStratification
) -> Vector{Symbol}

Generate transition names from UWD structure for age stratification models.

For transmission boxes with 4 ports (two inputs, two outputs), generates names capturing the age-to-age contact pattern in the format infectee_infector. This naming convention clearly identifies which age group is being infected (infectee) by which age group (infector), enabling interpretation of age-structured contact matrices.

For boxes with 2 ports (one input, one output), generates names in the format input_to_output. This fallback handles any non-standard box types that might appear in extended models.

Arguments

  • uwd: The undirected wiring diagram with age group names in junction :variable attributes
  • model::AgeStratification: The age stratification model (used for dispatch)

Returns

  • Vector{Symbol}: Vector of transition names, one per box in the UWD

Examples

# Used internally by create_model
typing = OnePopulationTyping()
age_strat = AgeStratification([:child, :adult])
uwd = create_model_uwd(typing, age_strat)
names = generate_transition_names(uwd, age_strat)
# Returns: [:child_child, :child_adult, :adult_child, :adult_adult]
# Representing: child←child, child←adult, adult←child, adult←adult transmission

See also: create_model, ContactStratification, AgeStratification

AlgebraicEpiMech.generate_transition_names — Method.

generate_transition_names(
    uwd,
    model::ImmuneHistory
) -> Vector{Symbol}

Generate transition names for the ImmuneHistory stratification factor. These become the second component of the composed typed_product transition names.

  • :transmission (4-port) → infect_<strain>_<susceptible-class>_by_<infector-class>, exposing the infecting strain (recovered from the infector junction) so the composed name stays keyable per strain.
  • reflexive / reversion boxes (2–3 port) → the first junction's variable, mirroring ContactStratification — e.g. the :disease reflexives and the off-diagonal :reversion on (h,i) are named by that (h,i), and :waning by its U_h. Composition with the base disambiguates them (base name differs).

Examples

factor = create_model_uwd(UninfectedInfectedTyping(), ImmuneHistory([:current, :invader]))
names = generate_transition_names(factor, ImmuneHistory([:current, :invader]))
# transmission names begin `infect_current…` / `infect_invader…`

AlgebraicEpiMech.observation_layout — Method.

observation_layout(pn; prefix) -> ObservationLayout
observation_layout(pn::LabelledPetriNet; prefix::Symbol = :O)

Return deterministic observation-chain metadata for an augmented Petri net.

The result is an ObservationLayout with fields:

  • obs_names: all flattened observation state names in Petri-net order
  • chains: ordered ObservationChainLayout values
  • cumulative_names: flattened terminal observation state for each chain

This centralizes the observation naming/ordering contract, so downstream packages do not each re-implement observation-state parsing. prefix must match the prefix passed to attach_observation. The function recognises the <prefix>_<source>_<stage> leaf that attach_observation produces; a chain named otherwise is not recognised as one.

AlgebraicEpiMech.type_system — Method.

type_system(
    typing::EpidemiologicalTyping,
    population_transitions...
) -> AlgebraicPetri.LabelledPetriNet
type_system(typing::EpidemiologicalTyping, population_transitions...)

Materialize an epidemiological typing strategy as the LabelledPetriNet used as the codomain of a typed Petri net. Concrete EpidemiologicalTyping subtypes must implement this interface.

AlgebraicEpiMech.type_system — Method.

type_system(
    typing::OnePopulationTyping,
    population_transitions...
) -> AlgebraicPetri.LabelledPetriNet
type_system(typing::OnePopulationTyping, population_transitions...)

Create the single-population Petri-net type system described by typing. Additional transition specifications are appended to the standard :transmission, :disease, :reversion, and :waning transitions.

Examples

typing = OnePopulationTyping(population_type = :Individual)
codomain = type_system(
    typing,
    :birth => (:Individual => (:Individual, :Individual)),
)

AlgebraicEpiMech.type_system — Method.

type_system(
    typing::UninfectedInfectedTyping,
    population_transitions...
) -> AlgebraicPetri.LabelledPetriNet
type_system(typing::UninfectedInfectedTyping, population_transitions...)

Create the uninfected/infected Petri-net type system described by typing. Additional transition specifications are appended to the standard :transmission, :disease, :reversion, and :waning transitions.

Examples

typing = UninfectedInfectedTyping(
    uninfected_type = :Susceptible,
    infected_type = :Infectious,
)
codomain = type_system(
    typing,
    :vaccination => (:Susceptible => :Susceptible),
)

AlgebraicEpiMech.vectorfield_flat — Method.

vectorfield_flat(
    pn::AlgebraicPetri.AbstractPetriNet
) -> AlgebraicEpiMech.var"#vectorfield!#vectorfield_flat##8"{AlgebraicEpiMech._NamePositions, AlgebraicEpiMech._NamePositions, AlgebraicEpiMech._NamePositions, Vector{Vector{Tuple{Int64, Int64}}}, Vector{Vector{Tuple{Int64, Int64}}}, Vector{T}, Vector{T1}, Int64, Int64} where {T, T1}

Generate an ODE vectorfield function from a Petri net using mass action kinetics as per AlgebraicPetri.vectorfield. The only difference is that species and transition names are flattened using flatten_symbols, converting nested tuples into single symbols joined by underscores.

Arguments

  • pn::AbstractPetriNet: A Petri net representing the reaction network

Returns

A function (du, u, p, t) -> du where:

  • du: Array/Dict to store computed derivatives
  • u: Current state (species concentrations), indexed by flattened species names
  • p: Parameters (rate constants), indexed by flattened transition names
  • t: Current time

This function complies with SciML conventions for in-place ODE vectorfields.

Performance

Everything that can be resolved from the net alone — flattened names, the mass-action input lists, the nonzero stoichiometry — is resolved once at build time, and the right-hand side is a sparse loop over arcs rather than a dense loop over transitions × species. Name lookup into the arguments is also done once per concrete argument type: u, du and p may be anything whose propertynames lists the flattened names in the same order as its integer indexing (LVector, NamedTuple, ...), and that position map is cached on first sight of the type; an AbstractDict is looked up by name on every call.