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:
utilizationtime-weighted fraction of capacity in usemean_queue_lengthtime-weighted average queue lengthmean_waitmean time waiters spent queued before being servedwait_timesthe 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
- examples/car.py — minimal process/sleep loop
- examples/jobs.py — Thompson-sampling job router
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ordo_des-0.1.1.tar.gz.
File metadata
- Download URL: ordo_des-0.1.1.tar.gz
- Upload date:
- Size: 29.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8461492d51de58aedd72be7d120672e03cad0175d970474247ed1ae988bf1e02
|
|
| MD5 |
890528f437b8caf38dbafd63e96fedab
|
|
| BLAKE2b-256 |
4aaea35b9667a406331e9e619e5fe3191746fcfb6b9bdabe9693e579613c6e6f
|
File details
Details for the file ordo_des-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ordo_des-0.1.1-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb0039e46d87d2ed208bed40324c536ab4c1670eacba1cabcabdb64ef50994ff
|
|
| MD5 |
b0b30e520e5521cfeb58ceb9bc976a35
|
|
| BLAKE2b-256 |
c5e4726bf3bc2aa1b5b48b1482231bc090641db6ea027dbbc12d552f147012e8
|