Skip to main content

Ordo

A discrete-event simulator for Python, built on async/await. Processes are plain coroutines that suspend on sleep(), shared resources, queues, or other events, and a single-threaded event loop drives them forward in simulated time.

Install

pip install ordo-des
from ordo import Simulator

Quick start

from ordo import Simulator

sim = Simulator()
sim.schedule_call(delay=1.0, func=lambda: print("hello"))
sim.run(until=10.0)

Processes

A process is any coroutine started with sim.process(...). It can pause with await sim.sleep(delay) and simulated time advances around it:

from ordo import Simulator

class Car:
    def __init__(self, sim):
        self.sim = sim
        sim.process(self.run())

    async def run(self):
        while True:
            print(f"parking at {self.sim.now}")
            await self.sim.sleep(5)
            print(f"driving at {self.sim.now}")
            await self.sim.sleep(2)

sim = Simulator()
Car(sim)
sim.run(until=15)

sim.process(coro) returns a Process (itself an Event) that fires with the coroutine's return value on completion, or fails with the exception it raised. Awaiting a Process lets one process wait on another.

Events

Event is the low-level primitive everything else builds on: a one-shot signal that coroutines can await. It fires exactly once, via succeed(value) or fail(exception), and any callback/coroutine waiting on it resumes then.

from ordo import Event

sim.any_of([event_a, event_b])   # fires when the first of these fires
sim.all_of([event_a, event_b])   # fires once every one of these has fired

Resources

Resource models a mutex/semaphore with limited capacity — e.g. a fixed pool of workers or machines:

res = sim.resource(capacity=2)

async def task(sim, res):
    async with res.acquire() as got:
        await sim.sleep(3)  # do work while holding a slot
# slot is released automatically on block exit, even on exception

acquire() also works as a plain awaitable (got = await res.acquire(), paired with a manual res.release()), supports priority (lower value is served first) and an optional timeout, after which the caller gives up its place in line and the await resolves to False instead of True.

Every Resource exposes .stats (see Stats) for utilization, queue length, and wait-time tracking.

Stores

Store is a bounded FIFO queue with blocking put/get, for producer/consumer pipelines:

from ordo import Store, TIMEOUT

store = Store(sim, capacity=10)

async def producer(sim, store):
    await store.put("item")

async def consumer(sim, store):
    item = await store.get(timeout=5)
    if item is TIMEOUT:
        print("gave up waiting")

put/get accept priority and timeout just like Resource.acquire; a timed-out get() resolves to the TIMEOUT sentinel (distinct from any legitimate item value, including None).

Stats

Resource and Store both expose a .stats object (UsageStats) with:

  • utilization time-weighted fraction of capacity in use
  • mean_queue_length time-weighted average queue length
  • mean_wait mean time waiters spent queued before being served
  • wait_times the raw list of recorded wait samples

These are computed lazily against sim.now, so they're accurate even if queried mid-simulation.

Interrupts

Any running process can be interrupted from the outside:

proc = sim.process(worker())
proc.interrupt(cause="cancelled")

This raises Interrupt(cause) inside the coroutine at its next suspension point. Ordo's internal bookkeeping (generation counters on each Process) ensures a stale, already-scheduled resumption from before the interrupt is never delivered on top of it, and any Resource/Store wait the coroutine abandoned mid-await is cleaned up automatically (a queued waiter is dequeued; an already-granted-but-undelivered slot or item is handed back).

An unhandled exception (other than Interrupt) that escapes a fire-and-forget process (one nobody is awaiting) is re-raised from sim.run() wrapped in a SimulationError.

Bayesian belief tracking

GammaExponentialBelief is an online Gamma-Exponential conjugate model for learning an unknown event rate (e.g. a server's true service rate) from observed inter-event delays — handy for adaptive routing policies inside a simulation:

from ordo import GammaExponentialBelief

belief = GammaExponentialBelief(shape=2.0, rate=10.0)  # prior
belief.observe(observed_delay)                     # update after each observation

belief.mean            # posterior mean of the rate (lambda)
belief.variance        # posterior variance
belief.expected_delay  # posterior mean delay (1 / lambda), inf if undefined
belief.sample_rate(rng)  # Thompson-sampling draw from the posterior

See examples/jobs.py for a full Thompson-sampling job router built on this.

Reproducibility

Each Simulator owns its own seeded numpy.random.Generator:

sim = Simulator(seed=42)
sim.rng.exponential(scale=1.0)

Running until a time or an event

sim.run(until=100.0)        # run until no event remains at or before t=100
sim.run(until=some_event)   # run until that event fires (or the heap drains)
sim.peek()                  # time of the next scheduled event, or inf
len(sim)                    # number of events currently pending

Examples

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ordo_des-0.1.0.tar.gz (29.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ordo_des-0.1.0-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

Details for the file ordo_des-0.1.0.tar.gz.

File metadata

  • Download URL: ordo_des-0.1.0.tar.gz
  • Upload date:
  • Size: 29.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.10

File hashes

Hashes for ordo_des-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d421dbc1ffaf52dc8ec8a79cc378e36ea91e9c83120328a7ea01369418df4708
MD5 6098fb5f86d7f8c0b1e5a51e418e01d3
BLAKE2b-256 e69db365d327d320be52c7cb23645b0442c4ee4ca479180c15a4de831af438c2

See more details on using hashes here.

File details

Details for the file ordo_des-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ordo_des-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.10

File hashes

Hashes for ordo_des-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9d97fdc49feb8c1a730f4f5f0b84517c2f35ce0471b7ba843a19f92c4a354ed9
MD5 e5bfbfc37e63ac9a8b290b70c98faa92
BLAKE2b-256 da5a8606e0679670dbf01bcf245b9ce59e57109c2dfdbe1d92df4179664cfdcf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page