simulsi 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. {simulsi-0.2.0 → simulsi-0.3.0}/.gitignore +1 -0
  2. {simulsi-0.2.0 → simulsi-0.3.0}/CHANGELOG.md +34 -0
  3. {simulsi-0.2.0 → simulsi-0.3.0}/PKG-INFO +23 -5
  4. {simulsi-0.2.0 → simulsi-0.3.0}/README.md +19 -4
  5. {simulsi-0.2.0 → simulsi-0.3.0}/benchmarks/README.md +33 -0
  6. simulsi-0.3.0/docs/api/analysis.md +6 -0
  7. simulsi-0.3.0/docs/api/decisions.md +5 -0
  8. simulsi-0.3.0/docs/api/entities.md +3 -0
  9. simulsi-0.3.0/docs/api/events.md +3 -0
  10. simulsi-0.3.0/docs/api/experiments.md +7 -0
  11. simulsi-0.3.0/docs/api/io.md +5 -0
  12. simulsi-0.3.0/docs/api/metrics.md +3 -0
  13. simulsi-0.3.0/docs/api/models.md +7 -0
  14. simulsi-0.3.0/docs/api/processes.md +11 -0
  15. simulsi-0.3.0/docs/api/randomness.md +7 -0
  16. simulsi-0.3.0/docs/api/resources.md +9 -0
  17. simulsi-0.3.0/docs/api/simulation.md +9 -0
  18. simulsi-0.3.0/docs/api/statistics.md +3 -0
  19. simulsi-0.3.0/docs/api/validation.md +5 -0
  20. simulsi-0.3.0/docs/api/visualization.md +3 -0
  21. {simulsi-0.2.0 → simulsi-0.3.0}/docs/architecture.md +2 -2
  22. {simulsi-0.2.0 → simulsi-0.3.0}/docs/cli.md +15 -3
  23. {simulsi-0.2.0 → simulsi-0.3.0}/docs/concepts.md +170 -0
  24. {simulsi-0.2.0 → simulsi-0.3.0}/docs/examples.md +8 -8
  25. {simulsi-0.2.0 → simulsi-0.3.0}/docs/experiments.md +72 -5
  26. {simulsi-0.2.0 → simulsi-0.3.0}/docs/faq.md +1 -1
  27. {simulsi-0.2.0 → simulsi-0.3.0}/docs/getting-started.md +1 -2
  28. {simulsi-0.2.0 → simulsi-0.3.0}/docs/index.md +23 -4
  29. {simulsi-0.2.0 → simulsi-0.3.0}/docs/research.md +7 -4
  30. {simulsi-0.2.0 → simulsi-0.3.0}/pyproject.toml +1 -0
  31. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/__init__.py +2 -1
  32. simulsi-0.3.0/src/simulsi/_version.py +1 -0
  33. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/analysis/__init__.py +5 -0
  34. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/analysis/comparison.py +70 -32
  35. simulsi-0.3.0/src/simulsi/analysis/warmup.py +162 -0
  36. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/benchmarks.py +8 -1
  37. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/cli/main.py +139 -6
  38. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/config/schema.py +4 -0
  39. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/core/model.py +32 -7
  40. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/core/simulation.py +71 -5
  41. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/experiments/__init__.py +2 -0
  42. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/experiments/experiment.py +169 -15
  43. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/introspection/graph.py +1 -0
  44. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/metrics/collectors.py +1 -1
  45. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/processes/__init__.py +15 -0
  46. simulsi-0.3.0/src/simulsi/processes/flow.py +107 -0
  47. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/processes/process.py +12 -3
  48. simulsi-0.3.0/src/simulsi/processes/schedules.py +227 -0
  49. simulsi-0.3.0/src/simulsi/processes/trace.py +118 -0
  50. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/queues/discipline.py +7 -0
  51. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/queues/queue.py +39 -13
  52. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/randomness/__init__.py +7 -1
  53. simulsi-0.3.0/src/simulsi/randomness/fitting.py +337 -0
  54. simulsi-0.3.0/src/simulsi/randomness/inverse.py +100 -0
  55. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/randomness/stream.py +33 -7
  56. simulsi-0.3.0/src/simulsi/resources/__init__.py +4 -0
  57. simulsi-0.3.0/src/simulsi/resources/container.py +228 -0
  58. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/resources/resource.py +9 -6
  59. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/scenarios/scenario.py +2 -2
  60. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/statistics/__init__.py +8 -0
  61. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/statistics/core.py +127 -2
  62. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/web/server.py +45 -5
  63. simulsi-0.3.0/src/simulsi/web/static/assets/index-DCyzlFeN.js +9 -0
  64. simulsi-0.3.0/src/simulsi/web/static/assets/index-quY_3V8w.css +2 -0
  65. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/web/static/index.html +2 -2
  66. {simulsi-0.2.0 → simulsi-0.3.0}/tests/cli/test_cli.py +11 -0
  67. {simulsi-0.2.0 → simulsi-0.3.0}/tests/integration/test_experiments.py +70 -0
  68. {simulsi-0.2.0 → simulsi-0.3.0}/tests/integration/test_web_server.py +11 -2
  69. simulsi-0.3.0/tests/unit/test_fitting_and_traces.py +178 -0
  70. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_model.py +12 -0
  71. simulsi-0.3.0/tests/unit/test_modelling_primitives.py +382 -0
  72. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_statistics.py +14 -0
  73. simulsi-0.3.0/tests/unit/test_variance_reduction.py +113 -0
  74. simulsi-0.3.0/tests/unit/test_warmup.py +57 -0
  75. {simulsi-0.2.0 → simulsi-0.3.0}/web/README.md +2 -1
  76. simulsi-0.2.0/src/simulsi/_version.py +0 -1
  77. simulsi-0.2.0/src/simulsi/resources/__init__.py +0 -3
  78. simulsi-0.2.0/src/simulsi/web/static/assets/index-CNMcVqxw.css +0 -2
  79. simulsi-0.2.0/src/simulsi/web/static/assets/index-DDtrYZla.js +0 -9
  80. {simulsi-0.2.0 → simulsi-0.3.0}/LICENSE +0 -0
  81. {simulsi-0.2.0 → simulsi-0.3.0}/NOTICE +0 -0
  82. {simulsi-0.2.0 → simulsi-0.3.0}/docs/extending.md +0 -0
  83. {simulsi-0.2.0 → simulsi-0.3.0}/docs/visualization.md +0 -0
  84. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/__main__.py +0 -0
  85. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/analysis/report.py +0 -0
  86. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/analysis/sensitivity.py +0 -0
  87. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/cli/__init__.py +0 -0
  88. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/cli/templates.py +0 -0
  89. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/config/__init__.py +0 -0
  90. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/core/__init__.py +0 -0
  91. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/core/checkpoint.py +0 -0
  92. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/core/clock.py +0 -0
  93. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/core/trace.py +0 -0
  94. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/cost/__init__.py +0 -0
  95. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/cost/model.py +0 -0
  96. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/entities/__init__.py +0 -0
  97. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/entities/entity.py +0 -0
  98. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/errors.py +0 -0
  99. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/events/__init__.py +0 -0
  100. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/events/event.py +0 -0
  101. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/experiments/montecarlo.py +0 -0
  102. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/experiments/provenance.py +0 -0
  103. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/introspection/__init__.py +0 -0
  104. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/metrics/__init__.py +0 -0
  105. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/models/__init__.py +0 -0
  106. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/models/queueing.py +0 -0
  107. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/optimization/__init__.py +0 -0
  108. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/optimization/objective.py +0 -0
  109. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/processes/disruption.py +0 -0
  110. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/py.typed +0 -0
  111. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/queues/__init__.py +0 -0
  112. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/randomness/distributions.py +0 -0
  113. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/scenarios/__init__.py +0 -0
  114. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/serialization/__init__.py +0 -0
  115. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/serialization/io.py +0 -0
  116. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/validation/__init__.py +0 -0
  117. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/validation/validate.py +0 -0
  118. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/visualization/__init__.py +0 -0
  119. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/visualization/plots.py +0 -0
  120. {simulsi-0.2.0 → simulsi-0.3.0}/src/simulsi/web/__init__.py +0 -0
  121. {simulsi-0.2.0 → simulsi-0.3.0}/tests/docs/test_docs.py +0 -0
  122. {simulsi-0.2.0 → simulsi-0.3.0}/tests/integration/test_analysis_features.py +0 -0
  123. {simulsi-0.2.0 → simulsi-0.3.0}/tests/integration/test_examples.py +0 -0
  124. {simulsi-0.2.0 → simulsi-0.3.0}/tests/integration/test_visualization.py +0 -0
  125. {simulsi-0.2.0 → simulsi-0.3.0}/tests/performance/test_performance.py +0 -0
  126. {simulsi-0.2.0 → simulsi-0.3.0}/tests/property/test_invariants.py +0 -0
  127. {simulsi-0.2.0 → simulsi-0.3.0}/tests/statistical/test_queueing_theory.py +0 -0
  128. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_checkpoint.py +0 -0
  129. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_entities.py +0 -0
  130. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_events.py +0 -0
  131. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_metrics.py +0 -0
  132. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_montecarlo_and_engine_extras.py +0 -0
  133. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_processes.py +0 -0
  134. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_queues.py +0 -0
  135. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_randomness.py +0 -0
  136. {simulsi-0.2.0 → simulsi-0.3.0}/tests/unit/test_resources.py +0 -0
@@ -28,3 +28,4 @@ src/simulsi/web/static/
28
28
  .DS_Store
29
29
  .idea/
30
30
  .vscode/
31
+ site/
@@ -6,6 +6,40 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.0] - 2026-10-06
10
+
11
+ ### Added
12
+
13
+ - Per-run warm-up override (`Model.simulate(warmup=)`, `simulsi run --warmup`);
14
+ the model warm-up is scaled proportionally for shorter runs, with a note.
15
+ - Dashboard runs experiments as background jobs with a progress bar
16
+ (`POST /api/run` returns 202, `GET /api/jobs/{id}`).
17
+ - Multiple-comparison adjustment in `compare(adjust="bonferroni"|"holm"|"bh")`,
18
+ the CLI (`--adjust`), configs and the dashboard.
19
+ - Warm-up detection with MSER-5 (`simulsi.analysis.mser`, `suggest_warmup`,
20
+ `simulsi warmup`).
21
+ - Sequential experiments: `Experiment.run_until(relative_precision, ...)` and
22
+ `simulsi experiment --until-precision`.
23
+ - Antithetic variates (`Experiment(antithetic=True)`, inverse-transform
24
+ sampling modes on `RandomStream`) and control variates
25
+ (`ExperimentResult.control_variate`, `statistics.control_variate`).
26
+ - Documentation site with API reference (mkdocs-material, GitHub Pages).
27
+ - Modelling primitives: `Container` (`sim.container`) for levels and stock;
28
+ filtered gets on queues (`queue.get(filter=...)`); `sim.wait_until(predicate)`;
29
+ shift schedules (`capacity_schedule`); non-stationary arrivals
30
+ (`arrivals` with `PiecewiseRate` or a rate function); `batch`, `split`/`join`,
31
+ `Router` and `shortest_queue`.
32
+ - Input modelling: `fit_distribution` (MLE fits ranked by AIC/BIC/KS, with
33
+ `FitReport.format()` and `plot()`), `load_values` and `simulsi fit`;
34
+ trace-driven input with `load_trace`, `trace_arrivals` and `TraceReplay`.
35
+
36
+ ### Changed
37
+
38
+ - Faster process engine (about 14% more events per second on process models);
39
+ `benchmarks/compare_simpy.py` compares against SimPy.
40
+ - Worker process pools are reused across experiments in the same process
41
+ (`shutdown_workers()` to release them).
42
+
9
43
  ## [0.2.0] - 2026-10-02
10
44
 
11
45
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simulsi
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A general-purpose, reproducible simulation and scenario experimentation framework for Python.
5
5
  Project-URL: Homepage, https://github.com/Yashjindal11/simulsi
6
6
  Project-URL: Documentation, https://github.com/Yashjindal11/simulsi/tree/main/docs
@@ -42,6 +42,9 @@ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
42
42
  Requires-Dist: pytest>=8.0; extra == 'dev'
43
43
  Requires-Dist: ruff==0.16.10; extra == 'dev'
44
44
  Requires-Dist: types-pyyaml; extra == 'dev'
45
+ Provides-Extra: docs
46
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
47
+ Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
45
48
  Provides-Extra: pandas
46
49
  Requires-Dist: pandas>=2.1; extra == 'pandas'
47
50
  Provides-Extra: parquet
@@ -55,6 +58,12 @@ Description-Content-Type: text/markdown
55
58
 
56
59
  # SimulSI
57
60
 
61
+ [![CI](https://github.com/Yashjindal11/simulsi/actions/workflows/ci.yml/badge.svg)](https://github.com/Yashjindal11/simulsi/actions/workflows/ci.yml)
62
+ [![PyPI](https://img.shields.io/pypi/v/simulsi)](https://pypi.org/project/simulsi/)
63
+ [![Python](https://img.shields.io/pypi/pyversions/simulsi)](https://pypi.org/project/simulsi/)
64
+ [![Docs](https://github.com/Yashjindal11/simulsi/actions/workflows/docs.yml/badge.svg)](https://yashjindal11.github.io/simulsi/)
65
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
66
+
58
67
  **Simulation Intelligence** - *Model the system. Simulate the future.*
59
68
 
60
69
  SimulSI is a general-purpose, reproducible simulation and scenario
@@ -120,14 +129,19 @@ for commercial packages; see
120
129
 
121
130
  ## Installation
122
131
 
123
- SimulSI is not on PyPI yet; install from source (Python 3.11+):
132
+ Python 3.11+, from [PyPI](https://pypi.org/project/simulsi/):
133
+
134
+ ```bash
135
+ pip install simulsi # core (includes the CLI and the web dashboard)
136
+ pip install "simulsi[viz]" # + matplotlib plots
137
+ pip install "simulsi[all]" # + pandas, Parquet, matplotlib, Plotly
138
+ ```
139
+
140
+ From source (for development):
124
141
 
125
142
  ```bash
126
143
  git clone https://github.com/Yashjindal11/simulsi.git && cd simulsi
127
144
  python -m venv .venv
128
- .venv/bin/pip install -e . # core
129
- .venv/bin/pip install -e ".[viz]" # + matplotlib plots
130
- .venv/bin/pip install -e ".[all]" # + pandas, Parquet, matplotlib, Plotly
131
145
  .venv/bin/pip install -e ".[dev]" # everything needed to run the tests
132
146
  ```
133
147
 
@@ -176,6 +190,10 @@ python scripts/run_examples.py # all of them, quick mode
176
190
 
177
191
  ## Documentation
178
192
 
193
+ The full documentation, including an API reference for every public class
194
+ and function, is at **https://yashjindal11.github.io/simulsi/**. The same
195
+ pages are in [`docs/`](docs):
196
+
179
197
  - [Getting started](docs/getting-started.md) - installation, quickstart, first experiment
180
198
  - [Core concepts](docs/concepts.md) - simulation, events, entities, resources, queues, processes, randomness, metrics, disruptions, validation, introspection
181
199
  - [Experiments and analysis](docs/experiments.md) - scenarios, experiments, Monte Carlo, statistics, comparison, sensitivity, cost, optimisation
@@ -1,5 +1,11 @@
1
1
  # SimulSI
2
2
 
3
+ [![CI](https://github.com/Yashjindal11/simulsi/actions/workflows/ci.yml/badge.svg)](https://github.com/Yashjindal11/simulsi/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/simulsi)](https://pypi.org/project/simulsi/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/simulsi)](https://pypi.org/project/simulsi/)
6
+ [![Docs](https://github.com/Yashjindal11/simulsi/actions/workflows/docs.yml/badge.svg)](https://yashjindal11.github.io/simulsi/)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
8
+
3
9
  **Simulation Intelligence** - *Model the system. Simulate the future.*
4
10
 
5
11
  SimulSI is a general-purpose, reproducible simulation and scenario
@@ -65,14 +71,19 @@ for commercial packages; see
65
71
 
66
72
  ## Installation
67
73
 
68
- SimulSI is not on PyPI yet; install from source (Python 3.11+):
74
+ Python 3.11+, from [PyPI](https://pypi.org/project/simulsi/):
75
+
76
+ ```bash
77
+ pip install simulsi # core (includes the CLI and the web dashboard)
78
+ pip install "simulsi[viz]" # + matplotlib plots
79
+ pip install "simulsi[all]" # + pandas, Parquet, matplotlib, Plotly
80
+ ```
81
+
82
+ From source (for development):
69
83
 
70
84
  ```bash
71
85
  git clone https://github.com/Yashjindal11/simulsi.git && cd simulsi
72
86
  python -m venv .venv
73
- .venv/bin/pip install -e . # core
74
- .venv/bin/pip install -e ".[viz]" # + matplotlib plots
75
- .venv/bin/pip install -e ".[all]" # + pandas, Parquet, matplotlib, Plotly
76
87
  .venv/bin/pip install -e ".[dev]" # everything needed to run the tests
77
88
  ```
78
89
 
@@ -121,6 +132,10 @@ python scripts/run_examples.py # all of them, quick mode
121
132
 
122
133
  ## Documentation
123
134
 
135
+ The full documentation, including an API reference for every public class
136
+ and function, is at **https://yashjindal11.github.io/simulsi/**. The same
137
+ pages are in [`docs/`](docs):
138
+
124
139
  - [Getting started](docs/getting-started.md) - installation, quickstart, first experiment
125
140
  - [Core concepts](docs/concepts.md) - simulation, events, entities, resources, queues, processes, randomness, metrics, disruptions, validation, introspection
126
141
  - [Experiments and analysis](docs/experiments.md) - scenarios, experiments, Monte Carlo, statistics, comparison, sensitivity, cost, optimisation
@@ -57,3 +57,36 @@ variation. Re-run the script on your hardware rather than relying on them.
57
57
  serial runs either way (tested).
58
58
  * `tests/performance/test_performance.py` guards against regressions with
59
59
  generous floors (about 10x below these numbers) and a linear-scaling check.
60
+
61
+ ## Comparison with SimPy
62
+
63
+ [`compare_simpy.py`](compare_simpy.py) runs the same M/M/2 queue (arrival
64
+ rate 1.8, service rate 1.0, a generator process per customer, NumPy draws)
65
+ in SimulSI and in [SimPy](https://simpy.readthedocs.io/), and checks that
66
+ both produce the same mean wait.
67
+
68
+ ```bash
69
+ pip install simpy
70
+ python benchmarks/compare_simpy.py --customers 100000 --repeat 3
71
+ ```
72
+
73
+ Measured on 2026-10-06 (Python 3.12.15, SimulSI 0.3.0.dev, SimPy 4.1.2,
74
+ Apple silicon, best of 3):
75
+
76
+ | library | seconds | customers/s | mean wait |
77
+ |---|---:|---:|---:|
78
+ | SimulSI | 1.35 | 73,800 | 4.6536 |
79
+ | SimPy | 0.84 | 118,700 | 4.6536 |
80
+
81
+ The two give identical results, and SimulSI takes about 1.6x as long. Part of
82
+ the gap is extra work SimulSI does on every request and release: it records
83
+ time-weighted utilization and queue length, a wait-time tally and counters,
84
+ and tracks which process holds each unit so unreleased resources can be
85
+ reported. SimPy leaves that bookkeeping to the model. The rest is per-event
86
+ overhead (lazy cancellation, the event status machine). Profiling for this
87
+ comparison led to hot-path changes that cut the gap from 1.85x and made the
88
+ `processes` benchmark about 14% faster.
89
+
90
+ If raw speed on very large models matters most, SimPy (or a compiled
91
+ engine) is the better choice. SimulSI is aimed at the experiment layer:
92
+ replications, statistics, scenarios and analysis.
@@ -0,0 +1,6 @@
1
+ # Comparison and sensitivity
2
+
3
+ ::: simulsi.analysis.comparison
4
+
5
+ ::: simulsi.analysis.sensitivity
6
+ ::: simulsi.analysis.warmup
@@ -0,0 +1,5 @@
1
+ # Cost and optimisation
2
+
3
+ ::: simulsi.cost.model
4
+
5
+ ::: simulsi.optimization.objective
@@ -0,0 +1,3 @@
1
+ # Entities
2
+
3
+ ::: simulsi.entities.entity
@@ -0,0 +1,3 @@
1
+ # Events
2
+
3
+ ::: simulsi.events.event
@@ -0,0 +1,7 @@
1
+ # Experiments and Monte Carlo
2
+
3
+ ::: simulsi.experiments.experiment
4
+
5
+ ::: simulsi.experiments.montecarlo
6
+
7
+ ::: simulsi.experiments.provenance
@@ -0,0 +1,5 @@
1
+ # Configuration and I/O
2
+
3
+ ::: simulsi.config.schema
4
+
5
+ ::: simulsi.serialization.io
@@ -0,0 +1,3 @@
1
+ # Metrics
2
+
3
+ ::: simulsi.metrics.collectors
@@ -0,0 +1,7 @@
1
+ # Models and scenarios
2
+
3
+ ::: simulsi.core.model
4
+
5
+ ::: simulsi.scenarios.scenario
6
+
7
+ ::: simulsi.models.queueing
@@ -0,0 +1,11 @@
1
+ # Processes, schedules and flow
2
+
3
+ ::: simulsi.processes.process
4
+
5
+ ::: simulsi.processes.schedules
6
+
7
+ ::: simulsi.processes.flow
8
+
9
+ ::: simulsi.processes.trace
10
+
11
+ ::: simulsi.processes.disruption
@@ -0,0 +1,7 @@
1
+ # Randomness
2
+
3
+ ::: simulsi.randomness.stream
4
+
5
+ ::: simulsi.randomness.distributions
6
+
7
+ ::: simulsi.randomness.fitting
@@ -0,0 +1,9 @@
1
+ # Resources, containers and queues
2
+
3
+ ::: simulsi.resources.resource
4
+
5
+ ::: simulsi.resources.container
6
+
7
+ ::: simulsi.queues.queue
8
+
9
+ ::: simulsi.queues.discipline
@@ -0,0 +1,9 @@
1
+ # Simulation and clock
2
+
3
+ ::: simulsi.core.simulation
4
+
5
+ ::: simulsi.core.clock
6
+
7
+ ::: simulsi.core.trace
8
+
9
+ ::: simulsi.core.checkpoint
@@ -0,0 +1,3 @@
1
+ # Statistics
2
+
3
+ ::: simulsi.statistics.core
@@ -0,0 +1,5 @@
1
+ # Validation and introspection
2
+
3
+ ::: simulsi.validation.validate
4
+
5
+ ::: simulsi.introspection.graph
@@ -0,0 +1,3 @@
1
+ # Visualization
2
+
3
+ ::: simulsi.visualization.plots
@@ -106,7 +106,7 @@ model is pickled by reference (module + qualified name); models loaded from
106
106
  files by the CLI are re-imported in each worker by an initializer. Results
107
107
  come back in task order. Start-up cost (about a second with the `spawn`
108
108
  start method used on macOS and Windows) makes parallelism worthwhile only when
109
- replications take longer than that. See [benchmarks](../benchmarks/README.md).
109
+ replications take longer than that. See [benchmarks](https://github.com/Yashjindal11/simulsi/blob/main/benchmarks/README.md).
110
110
 
111
111
  ## Checkpointing
112
112
 
@@ -143,7 +143,7 @@ files are found automatically; otherwise pass `model=`.
143
143
  ## Performance
144
144
 
145
145
  The engine is pure Python. Measured numbers are in
146
- [benchmarks/README.md](../benchmarks/README.md). Practical guidance:
146
+ [benchmarks/README.md](https://github.com/Yashjindal11/simulsi/blob/main/benchmarks/README.md). Practical guidance:
147
147
 
148
148
  * Prefer `yield number` / `sim.use(...)` in processes; avoid creating
149
149
  entities you never inspect.
@@ -3,9 +3,11 @@
3
3
  ```text
4
4
  simulsi init [DIR] [--force]
5
5
  simulsi validate TARGET [-p KEY=VALUE ...] [--smoke T]
6
- simulsi run TARGET [-p KEY=VALUE ...] [--seed N] [--duration T] [-m METRIC ...] [--trace FILE] [--json]
7
- simulsi experiment CONFIG [-r N] [-w N] [--seed N] [-o DIR] [--checkpoint FILE] [-q]
8
- simulsi analyze RESULTS [-m METRIC ...] [--baseline NAME] [--confidence C] [--precision P] [--json]
6
+ simulsi run TARGET [-p KEY=VALUE ...] [--seed N] [--duration T] [--warmup T] [-m METRIC ...] [--trace FILE] [--json]
7
+ simulsi experiment CONFIG [-r N] [-w N] [--seed N] [-o DIR] [--checkpoint FILE] [--adjust METHOD] [--until-precision P] [-q]
8
+ simulsi analyze RESULTS [-m METRIC ...] [--baseline NAME] [--confidence C] [--precision P] [--adjust METHOD] [--json]
9
+ simulsi warmup TARGET [-p KEY=VALUE ...] [--series KEY] [-r N] [--duration T] [--bins N] [--json]
10
+ simulsi fit FILE [--column NAME] [--candidates NAME ...] [--criterion aic|bic|ks] [--json]
9
11
  simulsi benchmark [--sizes N ...] [--workers N ...] [--replications N] [--no-memory] [--json]
10
12
  simulsi visualize TARGET [-o DIR] [-m METRIC ...] [--backend matplotlib|plotly] [--format png|svg|pdf]
11
13
  simulsi ui [RESULTS ...] [--model REF ...] [--host H] [--port P] [--no-browser]
@@ -41,6 +43,12 @@ Parameters given with `-p key=value` are parsed as YAML scalars, so
41
43
  `experiment.json`, `replications.csv`, `summary.csv` (and Parquet if asked).
42
44
  * **analyze** reloads saved results: summary, comparison, provenance, and
43
45
  with `--precision 0.05` how many replications each metric needs.
46
+ * **warmup** suggests a warm-up period: it averages a recorded time series
47
+ (default: the first resource's queue length) over replications and applies
48
+ MSER-5.
49
+ * **fit** fits input distributions to a CSV column or a text file of
50
+ numbers, ranks them (AIC, BIC or KS) and prints the best one as a config
51
+ spec you can paste into `experiment.yaml`.
44
52
  * **benchmark** measures engine throughput (10k / 100k / 1M events),
45
53
  process-based model throughput and parallel experiment speed on your
46
54
  machine, with tracemalloc peak memory.
@@ -67,6 +75,10 @@ experiment:
67
75
  common_random_numbers: true # same seeds for every scenario (paired comparisons)
68
76
  confidence: 0.95
69
77
  on_error: raise # or "record" to keep going and store errors
78
+ multiple_comparisons: holm # none (default), bonferroni, holm or bh
79
+ target_precision: 0.05 # optional: add replications until +-5% (relative) ...
80
+ precision_metrics: [resource.server.wait.mean] # ... for these metrics (default: metrics)
81
+ max_replications: 500 # upper limit for precision-based stopping
70
82
 
71
83
  parameters: # base values for every scenario
72
84
  arrival_rate: 0.9
@@ -232,6 +232,132 @@ Metrics (prefix `queue.<name>.`): `mean_length`, `max_length`, `wait.mean/max`,
232
232
  `puts`, `gets`, `removed`, `throughput`, `length_final`. Items never vanish:
233
233
  `puts == gets + removed + length`.
234
234
 
235
+ `orders.get(filter=lambda o: o.startswith("rush"))` takes the first matching
236
+ item in discipline order (waiting until one arrives); a filtered get that is
237
+ waiting does not hold up gets for other items. `try_get(filter)` is the
238
+ non-blocking form.
239
+
240
+ ## Containers
241
+
242
+ A `Container` holds a *level* (fuel, stock, cash, parts in a bin) rather than
243
+ distinct items. `get(amount)` waits until enough is there; `put(amount)` waits
244
+ until there is room. Waiters are served strictly in arrival order.
245
+
246
+ ```python
247
+ from simulsi import Simulation
248
+
249
+ sim = Simulation(seed=2)
250
+ stock = sim.container("stock", capacity=200, init=120)
251
+
252
+
253
+ def demand(sim):
254
+ rng = sim.stream("demand")
255
+ while True:
256
+ yield 1.0
257
+ yield stock.get(rng.integers(5, 15))
258
+
259
+
260
+ def replenish(sim):
261
+ while True:
262
+ yield sim.wait_until(lambda: stock.level < 40) # reorder point
263
+ yield 3.0 # lead time
264
+ yield stock.put(150)
265
+
266
+
267
+ sim.process(demand(sim))
268
+ sim.process(replenish(sim))
269
+ m = sim.run(until=500).metrics
270
+ print(m["container.stock.level.mean"], m["container.stock.stockouts"])
271
+ ```
272
+
273
+ Metrics (prefix `container.<name>.`): `level.mean/min/max`, `level_final`,
274
+ `put_amount`, `get_amount`, `stockouts` (gets that had to wait),
275
+ `get_wait.mean`, `put_wait.mean`.
276
+
277
+ `sim.wait_until(predicate)` works with any model state: the predicate is
278
+ checked after every event until it is true, and the yield returns the time
279
+ it became true. Keep predicates cheap.
280
+
281
+ ## Schedules and time-varying arrivals
282
+
283
+ ```python
284
+ from simulsi import Simulation
285
+ from simulsi.processes import PiecewiseRate, arrivals, capacity_schedule
286
+
287
+ sim = Simulation(seed=4)
288
+ agents = sim.resource("agents", 2)
289
+ # shifts in minutes: 2 agents at night, 6 from 08:00, 4 from 17:00, every day
290
+ capacity_schedule(sim, agents, [(0, 2), (480, 6), (1020, 4)], period=1440)
291
+ # calls per minute by time of day
292
+ calls = PiecewiseRate([(0, 0.05), (480, 0.6), (720, 0.9), (1020, 0.3)], period=1440)
293
+
294
+
295
+ def call(sim, i):
296
+ yield from sim.use(agents, sim.stream("talk").exponential(6.0))
297
+
298
+
299
+ arrivals(sim, calls, call, stream="calls")
300
+ m = sim.run(until=7 * 1440).metrics
301
+ print(m["resource.agents.utilization"], m["resource.agents.wait.mean"])
302
+ ```
303
+
304
+ * `capacity_schedule(sim, resource, [(time, capacity), ...], period=None)`
305
+ changes capacity at the given offsets. Lowering capacity does not
306
+ interrupt work in progress; the new limit applies to later grants.
307
+ * `arrivals(sim, rate, spawn, stream=..., limit=None, until=None)` calls
308
+ `spawn(sim, i)` at every arrival and starts it as a process if it returns a
309
+ generator. `rate` can be a number (Poisson process), a `PiecewiseRate`
310
+ (exact, one random draw per arrival) or any function `rate(t)` together with
311
+ `max_rate=` (generated by thinning).
312
+
313
+ ## Batching, split/join and routing
314
+
315
+ ```python
316
+ from simulsi import Simulation
317
+ from simulsi.processes import Router, batch, join, shortest_queue, split
318
+
319
+ sim = Simulation(seed=5)
320
+ parts = sim.queue("parts")
321
+ lines = [sim.resource("line-a", 1), sim.resource("line-b", 1)]
322
+ inspection = Router(sim, "inspection", {"pass": 0.9, "rework": 0.1})
323
+
324
+
325
+ def supplier(sim):
326
+ for i in range(12):
327
+ yield 0.5
328
+ yield parts.put(i)
329
+
330
+
331
+ def finish(sim, line):
332
+ yield from sim.use(line, 2.0)
333
+ return inspection.choose()
334
+
335
+
336
+ def assembler(sim):
337
+ while True:
338
+ kit = yield from batch(parts, 4, timeout=5.0) # up to 4 parts
339
+ if not kit:
340
+ return
341
+ results = yield from join(split(sim, [finish(sim, shortest_queue(lines))
342
+ for _ in range(2)]))
343
+ print(sim.now, kit, results)
344
+
345
+
346
+ sim.process(supplier(sim))
347
+ sim.process(assembler(sim))
348
+ m = sim.run().metrics
349
+ print(m["route.inspection.pass"], m["route.inspection.rework"])
350
+ ```
351
+
352
+ * `batch(queue, size, timeout=None)` collects items (`yield from`); with a
353
+ timeout it returns whatever has arrived by then.
354
+ * `split(sim, generators)` starts parallel processes and `join(processes)`
355
+ waits for all of them and returns their return values in order (a failure
356
+ in any of them is raised in the joiner).
357
+ * `Router(sim, name, {route: probability})` draws from its own stream
358
+ (`route:<name>`) and counts `route.<name>.<route>`.
359
+ * `shortest_queue(resources)` picks the resource with the fewest waiting.
360
+
235
361
  ## Randomness
236
362
 
237
363
  ```python
@@ -258,6 +384,50 @@ In a simulation use `sim.stream("name")`: streams are keyed by name, so the
258
384
  arrival stream is the same whatever else the model draws. This is what makes
259
385
  common-random-number comparisons effective.
260
386
 
387
+ ## Input data: fitting and traces
388
+
389
+ Input distributions should come from data. `fit_distribution` fits the
390
+ exponential, gamma, lognormal, normal, uniform, triangular and Poisson
391
+ families by maximum likelihood and ranks them:
392
+
393
+ ```python
394
+ import numpy as np
395
+
396
+ from simulsi.randomness import fit_distribution
397
+
398
+ observed = np.random.default_rng(7).gamma(2.5, 1.6, size=400) # e.g. service times
399
+ report = fit_distribution(observed)
400
+ print(report.format()) # AIC, BIC, KS statistic and p-value per family
401
+ service = report.best.distribution # a simulsi Gamma, ready to sample
402
+ print(service, report.best.distribution.to_spec())
403
+ ```
404
+
405
+ * Families whose support does not fit the data (gamma or lognormal with
406
+ zeros, Poisson with fractions) are listed in `report.skipped`.
407
+ * KS p-values use parameters estimated from the same data, so they are
408
+ optimistic. With fewer than about 30 observations, or when the best family
409
+ is still rejected, use `Empirical(data)` instead.
410
+ * `report.plot()` overlays the top fits on a histogram.
411
+ * `simulsi fit data.csv --column service` does the same from the command
412
+ line and prints a spec for `experiment.yaml`.
413
+
414
+ **Trace-driven runs** replay history instead of sampling. With recorded
415
+ arrivals and service times, a valid model should reproduce the recorded
416
+ waits, which makes this a strong validation check:
417
+
418
+ ```py
419
+ from simulsi.processes import TraceReplay, load_trace, trace_arrivals
420
+
421
+ history = load_trace("arrivals.csv") # rows like {"time": 3.5, "service": 4.1}
422
+
423
+ def job(sim, record):
424
+ yield from sim.use(server, record["service"])
425
+
426
+ trace_arrivals(sim, history, job) # one arrival per row, at row["time"]
427
+ repair_times = TraceReplay([12.0, 30.5, 8.2], cycle=True)
428
+ repair_times.sample() # next recorded value
429
+ ```
430
+
261
431
  ## Metrics
262
432
 
263
433
  ```python
@@ -1,6 +1,6 @@
1
1
  # Examples
2
2
 
3
- Every example is a single file in [`examples/`](../examples) with a
3
+ Every example is a single file in [`examples/`](https://github.com/Yashjindal11/simulsi/blob/main/examples) with a
4
4
  module-level `model` and a `main(replications=...)` function. All data is
5
5
  synthetic. Run one with `python examples/<name>.py`, or all of them quickly
6
6
  with `python scripts/run_examples.py`. They also work with the CLI:
@@ -11,7 +11,7 @@ simulsi validate examples/manufacturing.py
11
11
  simulsi visualize examples/bank_queue.py --out plots
12
12
  ```
13
13
 
14
- ## 1. Bank queue - [`bank_queue.py`](../examples/bank_queue.py)
14
+ ## 1. Bank queue - [`bank_queue.py`](https://github.com/Yashjindal11/simulsi/blob/main/examples/bank_queue.py)
15
15
 
16
16
  Poisson arrivals, log-normal service times (specified by mean and coefficient
17
17
  of variation via `LogNormal.from_moments`), a pool of tellers, and customers
@@ -22,7 +22,7 @@ the reneging fraction, for baseline / four tellers / lunch rush / both,
22
22
  compared with paired CIs. It ends with replication advice: how many
23
23
  replications are needed for a +-5% estimate of the mean wait.
24
24
 
25
- ## 2. Emergency department - [`hospital.py`](../examples/hospital.py)
25
+ ## 2. Emergency department - [`hospital.py`](https://github.com/Yashjindal11/simulsi/blob/main/examples/hospital.py)
26
26
 
27
27
  Patients get an acuity level (`Categorical`), see a triage nurse, wait for a
28
28
  treatment room and then a doctor. Rooms and doctors use the `"priority"`
@@ -35,7 +35,7 @@ their acuity's target (a service level), doctor and room utilization. It
35
35
  compares an extra doctor with extra rooms - which one helps depends on which
36
36
  resource binds.
37
37
 
38
- ## 3. Warehouse - [`warehouse.py`](../examples/warehouse.py)
38
+ ## 3. Warehouse - [`warehouse.py`](https://github.com/Yashjindal11/simulsi/blob/main/examples/warehouse.py)
39
39
 
40
40
  Orders arrive with a time-varying rate (a non-homogeneous Poisson process
41
41
  generated by thinning), each with a Poisson number of lines. Pickers walk
@@ -45,7 +45,7 @@ and pick, then hand orders to packers through a `Queue` buffer.
45
45
  time and picker/packer utilization, comparing a seventh picker against
46
46
  faster picking from better slotting.
47
47
 
48
- ## 4. Manufacturing - [`manufacturing.py`](../examples/manufacturing.py)
48
+ ## 4. Manufacturing - [`manufacturing.py`](https://github.com/Yashjindal11/simulsi/blob/main/examples/manufacturing.py)
49
49
 
50
50
  Two machines in series with a finite buffer. A full buffer blocks machine 1
51
51
  while it still holds the finished job. Each machine fails at random
@@ -58,7 +58,7 @@ afterwards.
58
58
  costs). Scenarios: preventive maintenance (longer MTBF), a bigger buffer, a
59
59
  second technician.
60
60
 
61
- ## 5. Transportation - [`transportation.py`](../examples/transportation.py)
61
+ ## 5. Transportation - [`transportation.py`](https://github.com/Yashjindal11/simulsi/blob/main/examples/transportation.py)
62
62
 
63
63
  Buses circulate around a loop of stops. Passengers queue at each stop with a
64
64
  random destination; a bus alights passengers, boards as many as fit, dwells
@@ -69,7 +69,7 @@ buses (congestion) and load factor (capacity utilization). It compares a
69
69
  fourth bus with bigger buses that have the same total number of seats -
70
70
  frequency against capacity.
71
71
 
72
- ## 6. Aviation - [`aviation.py`](../examples/aviation.py)
72
+ ## 6. Aviation - [`aviation.py`](https://github.com/Yashjindal11/simulsi/blob/main/examples/aviation.py)
73
73
 
74
74
  A synthetic day of arrivals in two banks plus background traffic. Flights
75
75
  land with log-normal delays, wait on the taxiway if no gate is free (a gate
@@ -84,7 +84,7 @@ an airline planning tool, and it uses no real airline data.
84
84
 
85
85
  ## Smaller examples
86
86
 
87
- * [`queue.py`](../examples/queue.py) - M/M/c in 30 lines, compared with the
87
+ * [`queue.py`](https://github.com/Yashjindal11/simulsi/blob/main/examples/queue.py) - M/M/c in 30 lines, compared with the
88
88
  Erlang-C formula.
89
89
  * `simulsi init` - the starter "service desk" project with a YAML experiment
90
90
  and cost model.