Skip to content

Core engine

The sequential simulation core: the event loop, the generic event, the process driver, condition composition, and the error hierarchy. Every symbol here is re-exported from the top-level llmsim package.

Simulation

Sim

Sim(initial_time=0, *, seed=None, rng=None, debug=False)

A single-threaded discrete-event simulation.

Sim owns the event schedule, the current simulation time, and a reproducible random stream. It and every event, process, and resource attached to it belong to exactly one thread; parallelism comes from running many independent Sim instances, never from locking one (share-nothing architecture).

Parameters:

Name Type Description Default
initial_time float

The simulation clock's starting value.

0
seed int | None

Seed for :attr:rng. Ignored when rng is given. This is the single seam the Phase 2 seed tree injects a derived stream into.

None
rng Random | None

An explicit :class:random.Random to adopt as :attr:rng.

None
debug bool

When True (or when LLMSIM_DEBUG=1 is set in the environment), every :meth:schedule asserts it runs on the thread that created the Sim, catching accidental cross-thread sharing.

False

Initialize an empty schedule at initial_time.

now property

now

The current simulation time.

debug property

debug

Whether debug mode is on (debug=True or LLMSIM_DEBUG=1).

Core owns this question; features that must "flag loudly in debug mode" (a non-strict offload, for example) ask here instead of inferring it from the thread-ownership guard.

active_process property

active_process

The process whose event is currently being resumed, if any.

schedule

schedule(event, priority=NORMAL, delay=0)

Insert event into the schedule at now + delay with priority.

Raises:

Type Description
RuntimeError

in debug mode, if called from a thread other than the one that constructed this Sim.

peek

peek()

Return the time of the next scheduled event, or INFINITY.

step

step()

Process the next scheduled event, advancing the clock to its time.

Raises:

Type Description
EmptySchedule

if no events remain to process.

delay

delay(delay: float) -> Timeout[None]
delay(delay: float, value: T) -> Timeout[T]
delay(delay, value=None)

Return a :class:~llmsim.core.events.Timeout that fires after delay.

With no value the timeout resolves to None; otherwise it resolves to value.

event

event()

Return a fresh, untriggered :class:~llmsim.core.events.Event.

all_of

all_of(events)

Return a condition triggered once every event in events succeeds.

any_of

any_of(events)

Return a condition triggered once any event in events succeeds.

spawn

spawn(process, *args, **kwargs)

Start a new process and return its :class:~llmsim.core.process.Process.

process may be:

  • a process function -- called as process(sim, *args, **kwargs) so the Sim is injected as its first argument (design ergonomics); or
  • an already-created generator or coroutine, used as-is.

Both a def ... : yield generator and an async def coroutine are accepted; the same unified driver advances either.

offload

offload(fn, /, *args, delay=None, strict=True, **kwargs)

Run fn(*args, **kwargs) on the attached offload pool.

Returns an event that a process waits on like any other. In strict mode (the default) the result is delivered exactly at the deterministic completion slot now + delay, regardless of how long the computation takes on the wall clock; with strict=False it is delivered as soon as it is available (nondeterministic ordering), no earlier than now + delay when delay is given.

Parameters:

Name Type Description Default
fn Callable[..., T]

An importable module-level callable (validated at submission); its positional and keyword arguments follow, except the reserved keywords delay and strict.

required
delay float | None

The completion-slot offset, a pure function of model state (required in strict mode); under strict=False, an earliest-delivery lower bound.

None
strict bool

Whether delivery is pinned to the deterministic slot.

True

Raises:

Type Description
SimulationError

if no offload pool is attached to this Sim.

ValueError

if delay is negative, or omitted in strict mode.

run

run(until=None)

Advance the simulation until until is reached.

Parameters:

Name Type Description Default
until float | Event[Any] | None

None runs until the schedule empties. A number runs until the clock reaches that time. An :class:~llmsim.core.events.Event runs until that event is processed and returns its value.

None

Raises:

Type Description
ValueError

if until is a time at or before :attr:now.

RuntimeError

if until is an event that can never trigger because the schedule emptied first.

Events

Event

Event(sim)

Bases: Generic[T]

Something that may happen at a point in simulation time.

An event moves through three states: it may happen (:attr:triggered is False), it is going to happen (:attr:triggered is True once :meth:succeed, :meth:fail, or :meth:trigger schedules it), and it has happened (:attr:processed is True after the Sim invokes its callbacks).

Every event belongs to exactly one :class:~llmsim.core.sim.Sim and, with it, to exactly one thread. A failed event whose exception is never defused crashes the simulation when processed, so faults are never silently lost.

Event is generic in the value it yields on success. Because :meth:__await__ yields self exactly once, await event (in an async def process) and yield event (in a generator process) resolve through the very same scheduling path.

Create an untriggered event owned by sim.

sim property

sim

The :class:~llmsim.core.sim.Sim this event belongs to.

triggered property

triggered

True once the event has a value and is scheduled for processing.

processed property

processed

True once the event's callbacks have been invoked.

ok property

ok

Whether the event succeeded.

Raises:

Type Description
AttributeError

if accessed before the event is triggered.

value property

value

The event's success value (or the exception, if it failed).

Raises:

Type Description
AttributeError

if accessed before the event is triggered.

trigger

trigger(event)

Adopt event's outcome and schedule this event for processing.

Usable directly as a callback so one event's completion can drive a chain of dependent events.

succeed

succeed(value=None)

Mark the event successful with value and schedule it.

Returns:

Type Description
Event[T]

The event itself, for call chaining.

Raises:

Type Description
RuntimeError

if the event has already been triggered.

fail

fail(exception)

Mark the event failed with exception and schedule it.

Returns:

Type Description
Event[T]

The event itself, for call chaining.

Raises:

Type Description
ValueError

if exception is not a BaseException instance.

RuntimeError

if the event has already been triggered.

Timeout

Timeout(sim, delay, value=None)

Bases: Event[T]

An event that triggers automatically after a fixed delay elapses.

Constructing a timeout schedules it immediately; a process waits for the delay by yielding (or awaiting) it. This is what :meth:~llmsim.core.sim.Sim.delay returns.

Schedule a timeout that fires delay time units from now.

Raises:

Type Description
ValueError

if delay is negative.

Processes

Process

Process(sim, runnable)

Bases: Event[T]

A generator or coroutine advanced by the simulation, and itself an event.

A process suspends by yielding (or awaiting) an event; the driver resumes it with that event's value once it is processed, or throws the event's exception into it. Process is itself an :class:~llmsim.core.events.Event that is triggered when the body returns (its value becomes the return value) or raises (its value becomes the exception), so other processes can wait on a process the same way they wait on any event.

Schedule runnable to start at the next urgent step.

Raises:

Type Description
ValueError

if runnable is neither a generator nor a coroutine (it lacks a throw method).

target property

target

The event this process is currently waiting on, or None if dead.

is_alive property

is_alive

True until the process body returns or raises.

interrupt

interrupt(cause=None)

Interrupt this process, throwing an :class:Interrupt into it.

The interrupt is delivered at the process's next resume, ahead of the event it is currently waiting on.

Raises:

Type Description
RuntimeError

if the process has already terminated, or if a process tries to interrupt itself.

Condition composition

Condition

Condition(sim, evaluate, events)

Bases: Event[ConditionValue]

An event triggered once evaluate holds over its member events.

The condition's value is a :class:ConditionValue giving access to the members that had triggered by the time it was processed. If any member fails, the condition fails too: with that member's exception, or -- when several members have already failed together -- with a single :exc:ExceptionGroup aggregating them (a deliberate clean-break divergence from SimPy 3, which forwards only the first failure).

Watch events, triggering when evaluate returns True.

Raises:

Type Description
ValueError

if the members do not all belong to sim.

all_events staticmethod

all_events(events, count)

Return True once every event has been processed.

any_events staticmethod

any_events(events, count)

Return True once at least one event has been processed.

AllOf

AllOf(sim, events)

Bases: Condition

A condition triggered once all events have succeeded.

Fails immediately if any member fails.

Wait for every event in events.

AnyOf

AnyOf(sim, events)

Bases: Condition

A condition triggered once any of events has succeeded.

Fails immediately if any member fails.

Wait for at least one event in events.

Errors

SimulationError

Bases: Exception

Base class for every exception the simulation engine raises.

Catching SimulationError distinguishes engine faults (a stopped simulation, a misused event, an interrupt) from the domain exceptions a model raises out of its own process code.

Interrupt

Interrupt(cause=None)

Bases: SimulationError

Thrown into a process when another process interrupts it.

:attr:cause carries the reason for the interrupt, or None when the interrupter supplied none. When a process is interrupted several times concurrently, the interrupts are thrown in the order they were scheduled.

Store cause as the single exception argument.

cause property

cause

The reason for the interrupt, or None if none was provided.

EmptySchedule

Bases: SimulationError

Raised by :meth:~llmsim.core.sim.Sim.step when no events remain.

A bare :meth:~llmsim.core.sim.Sim.run (until=None) catches this to stop cleanly once the schedule drains; it surfaces to the caller only when a run(until=event) can never be satisfied because the queue emptied first.