simul is a discrete-event simulation library for running high-level simulations of real-world problems and for running simulated experiments.
simul is a discrete-event simulator using incremental time progression, with M/M/c queues for interactions between agents. It also supports some forms of experimentation and simulated annealing to replicate a simulation many times, varying the simulation parameters.
Use-cases:
- Discrete-event simulation
- Complex adaptive systems
- Simulated annealing
- Job-shop scheduling
- Birth-death processes
- Computer experiments
- Other: simulating logistics, operations research problems, running experiments to approximate a global optimum, simulating queueing systems, distributed systems, performance engineering/analysis, and so on.
Warning
Experimental and unstable. Almost all APIs are expected to change.
- For some examples, see the
examplessubdirectory. - For use cases where your agents need their own custom state, define a struct, implement
Agent, and pass your agents intoSimulationvia constructingAgentInitializers.
[dependencies] simul = "0.4.1"use simul::agent::*; use simul::Simulation; use simul::SimulationParameters; /// Example of a minimal, simple Simulation that can be executed. fn main() { // Runs a simulation with a producer that produces work at every tick of // discrete time (period=1), and a consumer that cannot keep up (can only // process that work every third tick). let mut simulation = Simulation::new(SimulationParameters { // We pass in two agents: // `producer`: produces a message to the consumer every tick // `consumer`: consumes w/ no side effects every second tick // Agents are powerful, and you can pass-in custom implementations here. agent_initializers: vec![ periodic_producing_agent("producer", 1, "consumer"), periodic_consuming_agent("consumer", 2), ], // We pass in a halt condition so the simulation knows when it is finished. // In this case, it is "when the simulation is 10 ticks old, we're done." halt_check: |s: &Simulation| s.time == 10, ..Default::default() }); // For massive simulations, you might block on this line for a long time. simulation.run(); // Post-simulation, you can do analytics on the stored metrics, data, etc. simulation .agents .iter() .for_each(|agent| println!("{:#?}", agent)); }- A simulation is a collection of
Agentsthat interact with each other viaMessages. - The simulation keeps a discrete time (u64) which is incremented on each tick of the Simulation.
- What an
Agentdoes at each tick of the simulation is provided by you in itson_tick()andon_message()methods. Agentsmust have a unique name.- If an
Agentwants to interact with anotherAgent, it can send aMessagevia the&mut ctx: AgentContextpassed intoon_tickandon_message.
The simulation runs all the logic of calling process(), distributing messages, tracking metrics, incrementing time, and when to halt. A Simulation is finished when the provided halt_check function returns true, or if an Agent responds with a special Interrupt to halt the Simulation.
Here's an example of an outputted graph from a simulation run. In this simulation, we show the average waiting time of customers in a line at a cafe. The customers arrive at a Poisson-distributed arrival rate (lambda<-60.0) and a Poisson-distributed coffee-serving rate with the same distribution.
This simulation maps to the real world by assuming one tick of discrete-simulation time is equal to one second.
Basically, the barista serves coffees at around 60 seconds per drink and the customers arrive at about the same rate, both modeled by a stochastic Poisson generator.
This simulation has a halt_check condition of the simulation's time being equal to 60*60*12, representing a full 12-hour day of the cafe being open.
Issues, bugs, features are tracked in TODO.org
