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
