18 unstable releases (3 breaking)
0.4.1 | Mar 31, 2024 |
---|---|
0.4.0 | Mar 31, 2024 |
0.3.3 | Mar 26, 2024 |
0.2.3 | Jul 7, 2023 |
0.1.7 | Jul 5, 2023 |
#247 in Math
145KB
559 lines
simul
is a discrete-event simulation library aimed at high-level use-cases to
quickly simulate real-world problems and run 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.
Usage
Warning
Experimental and unstable. Almost all APIs are expected to change.
Basic usage
[dependencies]
simul = "0.3.1"
use simul::Simulation;
use simul::agent::*;
// 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:
// one that produces -> consumer every tick
// one that simply consumes w/ no side effects every third tick
agents: vec![
periodic_producing_agent("producer".to_string(), 1, "consumer".to_string()),
periodic_consuming_agent("consumer".to_string(), 3),
],
// You can set the starting epoch for the simulation. 0 is normal.
starting_time: 0,
// Whether to collect telemetry on queue depths at every tick.
// Useful if you're interested in backlogs, bottlenecks, etc. Costs performance.
enable_queue_depth_metric: true,
/// Records a metric on the number of cycles agents were asleep for.
enable_agent_asleep_cycles_metric: true,
// 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,
});
simulation.run();
Poisson-distributed example w/ Plotting
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.
This is a code example for generating the above.
use plotters::prelude::*;
use rand_distr::Poisson;
use simul::agent::*;
use simul::*;
use std::path::PathBuf;
fn main() {
run_example_cafe_simulation();
}
fn run_example_cafe_simulation() -> Result<(), Box<dyn std::error::Error>> {
let mut simulation = Simulation::new(SimulationParameters {
agents: vec![
poisson_distributed_consuming_agent("Barista".to_string(), Poisson::new(60.0).unwrap()),
poisson_distributed_producing_agent(
"Customers".to_string(),
Poisson::new(60.0).unwrap(),
"Barista".to_string(),
),
],
starting_time: 0,
enable_queue_depth_metric: true,
halt_check: |s: &Simulation| s.time == 60 * 60 * 12,
});
simulation.run();
plot_queued_durations_for_processed_messages(
&simulation,
&["Barista".into()],
&"/tmp/cafe-example-queued-durations.png".to_string().into(),
)
}
Contributing
Issues, bugs, features are tracked in TODO.org
Dependencies
~1.2–1.9MB
~38K SLoC