SimPy with NumPy: Better Randomness and Faster Analysis
<p>Python's <code>random</code> module is fine. NumPy's random is better. More distributions. Better seeding. Faster analysis.</p> <h2 id="why-numpy">Why NumPy?</h2> <ul> <li>More probability distributions</li> <li>Better random number generation</li> <li>Reproducibility with modern API</li> <li>Fast array operations for analysis</li> </ul> <h2 id="numpys-random-generator">NumPy's Random Generator</h2> <div class="code-block"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">numpy</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">np</span> <span class="c1"># Modern API (recommended)</span> <span class="n">rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">seed</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span> <span class="c1"># Generate random numbers</span> <span class="n">rng</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="c1"># Uniform [0, 1)</span> <span class="n">rng</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="c1"># Uniform [5, 10]</span> <span class="n">rng</span><span class="o">.</span><span class="n">exponential</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># Exponential, mean 5</span> <span class="n">rng</span><span class="o">.</span><span class="n">normal</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1"># Normal, mean 10, std 2</span> <span class="n">rng</span><span class="o">.</span><span class="n">poisson</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># Poisson, lambda 5</span> <span class="n">rng</span><span class="o">.</span><span class="n">gamma</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1"># Gamma, shape 2, scale 2</span> <span class="n">rng</span><span class="o">.</span><span class="n">lognormal</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">)</span> <span class="c1"># Lognormal</span> <span class="n">rng</span><span class="o">.</span><span class="n">triangular</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1"># Triangular</span> <span class="n">rng</span><span class="o">.</span><span class="n">integers</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="c1"># Integer [1, 10)</span> </code></pre></div> <h2 id="distributions-for-simulation">Distributions for Simulation</h2> <h3 id="interarrival-times">Interarrival Times</h3> <div class="code-block"><pre><span></span><code><span class="c1"># Poisson process (exponential interarrivals)</span> <span class="n">rng</span><span class="o">.</span><span class="n">exponential</span><span class="p">(</span><span class="n">mean_interarrival</span><span class="p">)</span> <span class="c1"># Erlang (sum of exponentials)</span> <span class="n">rng</span><span class="o">.</span><span class="n">gamma</span><span class="p">(</span><span class="n">shape</span><span class="o">=</span><span class="n">k</span><span class="p">,</span> <span class="n">scale</span><span class="o">=</span><span class="n">mean</span><span class="o">/</span><span class="n">k</span><span class="p">)</span> </code></pre></div> <h3 id="service-times">Service Times</h3> <div class="code-block"><pre><span></span><code><span class="c1"># Exponential</span> <span class="n">rng</span><span class="o">.</span><span class="n">exponential</span><span class="p">(</span><span class="n">mean_service</span><span class="p">)</span> <span class="c1"># Lognormal (common for human tasks)</span> <span class="n">rng</span><span class="o">.</span><span class="n">lognormal</span><span class="p">(</span><span class="n">mu</span><span class="p">,</span> <span class="n">sigma</span><span class="p">)</span> <span class="c1"># Triangular (when you have min, max, mode)</span> <span class="n">rng</span><span class="o">.</span><span class="n">triangular</span><span class="p">(</span><span class="n">low</span><span class="p">,</span> <span class="n">high</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span> <span class="c1"># Gamma (flexible shape)</span> <span class="n">rng</span><span class="o">.</span><span class="n">gamma</span><span class="p">(</span><span class="n">shape</span><span class="p">,</span> <span class="n">scale</span><span class="p">)</span> </code></pre></div> <h3 id="discrete-choices">Discrete Choices</h3> <div class="code-block"><pre><span></span><code><span class="c1"># Choose from options</span> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'A'</span><span class="p">,</span> <span class="s1">'B'</span><span class="p">,</span> <span class="s1">'C'</span><span class="p">]</span> <span class="n">rng</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">options</span><span class="p">)</span> <span class="c1"># Weighted choice</span> <span class="n">weights</span> <span class="o">=</span> <span class="p">[</span><span class="mf">0.5</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">]</span> <span class="n">rng</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">options</span><span class="p">,</span> <span class="n">p</span><span class="o">=</span><span class="n">weights</span><span class="p">)</span> </code></pre></div> <h2 id="per-process-random-streams">Per-Process Random Streams</h2> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">Simulation</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">master_seed</span><span class="p">):</span> <span class="n">master_rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">master_seed</span><span class="p">)</span> <span class="c1"># Generate sub-seeds</span> <span class="n">seeds</span> <span class="o">=</span> <span class="n">master_rng</span><span class="o">.</span><span class="n">integers</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">**</span><span class="mi">31</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># Separate streams</span> <span class="bp">self</span><span class="o">.</span><span class="n">arrival_rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">seeds</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="bp">self</span><span class="o">.</span><span class="n">service_rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">seeds</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="bp">self</span><span class="o">.</span><span class="n">failure_rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">seeds</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="k">def</span><span class="w"> </span><span class="nf">next_arrival</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">arrival_rng</span><span class="o">.</span><span class="n">exponential</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">next_service</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">service_rng</span><span class="o">.</span><span class="n">lognormal</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">next_failure</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">failure_rng</span><span class="o">.</span><span class="n">exponential</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span> </code></pre></div> <h2 id="array-operations-for-analysis">Array Operations for Analysis</h2> <div class="code-block"><pre><span></span><code><span class="c1"># Collect wait times as numpy array</span> <span class="n">wait_times</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">stats</span><span class="p">[</span><span class="s1">'wait_times'</span><span class="p">])</span> <span class="c1"># Fast statistics</span> <span class="n">np</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">wait_times</span><span class="p">)</span> <span class="n">np</span><span class="o">.</span><span class="n">median</span><span class="p">(</span><span class="n">wait_times</span><span class="p">)</span> <span class="n">np</span><span class="o">.</span><span class="n">std</span><span class="p">(</span><span class="n">wait_times</span><span class="p">)</span> <span class="n">np</span><span class="o">.</span><span class="n">percentile</span><span class="p">(</span><span class="n">wait_times</span><span class="p">,</span> <span class="p">[</span><span class="mi">50</span><span class="p">,</span> <span class="mi">90</span><span class="p">,</span> <span class="mi">95</span><span class="p">,</span> <span class="mi">99</span><span class="p">])</span> <span class="n">np</span><span class="o">.</span><span class="n">min</span><span class="p">(</span><span class="n">wait_times</span><span class="p">)</span> <span class="n">np</span><span class="o">.</span><span class="n">max</span><span class="p">(</span><span class="n">wait_times</span><span class="p">)</span> <span class="c1"># Histogram</span> <span class="n">counts</span><span class="p">,</span> <span class="n">bins</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">histogram</span><span class="p">(</span><span class="n">wait_times</span><span class="p">,</span> <span class="n">bins</span><span class="o">=</span><span class="mi">30</span><span class="p">)</span> <span class="c1"># Correlation</span> <span class="n">np</span><span class="o">.</span><span class="n">corrcoef</span><span class="p">(</span><span class="n">wait_times</span><span class="p">,</span> <span class="n">service_times</span><span class="p">)</span> </code></pre></div> <h2 id="generating-correlated-random-variables">Generating Correlated Random Variables</h2> <div class="code-block"><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">generate_correlated</span><span class="p">(</span><span class="n">rng</span><span class="p">,</span> <span class="n">mean1</span><span class="p">,</span> <span class="n">mean2</span><span class="p">,</span> <span class="n">correlation</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Generate correlated exponential variables."""</span> <span class="c1"># Generate independent uniforms</span> <span class="n">u1</span> <span class="o">=</span> <span class="n">rng</span><span class="o">.</span><span class="n">random</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="n">u2</span> <span class="o">=</span> <span class="n">rng</span><span class="o">.</span><span class="n">random</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="c1"># Correlate via copula (simplified)</span> <span class="n">u2_corr</span> <span class="o">=</span> <span class="n">correlation</span> <span class="o">*</span> <span class="n">u1</span> <span class="o">+</span> <span class="n">np</span><span class="o">.</span><span class="n">sqrt</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">correlation</span><span class="o">**</span><span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">u2</span> <span class="c1"># Transform to exponential</span> <span class="n">x1</span> <span class="o">=</span> <span class="o">-</span><span class="n">mean1</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">u1</span><span class="p">)</span> <span class="n">x2</span> <span class="o">=</span> <span class="o">-</span><span class="n">mean2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">u2_corr</span><span class="p">)</span> <span class="k">return</span> <span class="n">x1</span><span class="p">,</span> <span class="n">x2</span> </code></pre></div> <h2 id="batch-statistics">Batch Statistics</h2> <div class="code-block"><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">batch_means</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">batch_size</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Calculate batch means for confidence intervals."""</span> <span class="n">data</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="n">n_batches</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="o">//</span> <span class="n">batch_size</span> <span class="n">batches</span> <span class="o">=</span> <span class="n">data</span><span class="p">[:</span><span class="n">n_batches</span> <span class="o">*</span> <span class="n">batch_size</span><span class="p">]</span><span class="o">.</span><span class="n">reshape</span><span class="p">(</span><span class="n">n_batches</span><span class="p">,</span> <span class="n">batch_size</span><span class="p">)</span> <span class="n">batch_means</span> <span class="o">=</span> <span class="n">batches</span><span class="o">.</span><span class="n">mean</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="k">return</span> <span class="p">{</span> <span class="s1">'overall_mean'</span><span class="p">:</span> <span class="n">batch_means</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="s1">'std_error'</span><span class="p">:</span> <span class="n">batch_means</span><span class="o">.</span><span class="n">std</span><span class="p">()</span> <span class="o">/</span> <span class="n">np</span><span class="o">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">n_batches</span><span class="p">),</span> <span class="s1">'batch_means'</span><span class="p">:</span> <span class="n">batch_means</span> <span class="p">}</span> </code></pre></div> <h2 id="empirical-distribution">Empirical Distribution</h2> <div class="code-block"><pre><span></span><code><span class="k">class</span><span class="w"> </span><span class="nc">EmpiricalDistribution</span><span class="p">:</span> <span class="w"> </span><span class="sd">"""Sample from historical data."""</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">rng</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">rng</span> <span class="o">=</span> <span class="n">rng</span> <span class="ow">or</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">()</span> <span class="k">def</span><span class="w"> </span><span class="nf">sample</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">rng</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">sample_n</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span> <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">rng</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="n">n</span><span class="p">,</span> <span class="n">replace</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="c1"># Usage with historical data</span> <span class="n">historical_service_times</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="mf">4.2</span><span class="p">,</span> <span class="mf">5.1</span><span class="p">,</span> <span class="mf">3.8</span><span class="p">,</span> <span class="mf">6.2</span><span class="p">,</span> <span class="mf">4.9</span><span class="p">,</span> <span class="o">...</span><span class="p">])</span> <span class="n">service_dist</span> <span class="o">=</span> <span class="n">EmpiricalDistribution</span><span class="p">(</span><span class="n">historical_service_times</span><span class="p">,</span> <span class="n">rng</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">customer</span><span class="p">(</span><span class="n">env</span><span class="p">):</span> <span class="k">yield</span> <span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">service_dist</span><span class="o">.</span><span class="n">sample</span><span class="p">())</span> </code></pre></div> <h2 id="fitting-distributions">Fitting Distributions</h2> <div class="code-block"><pre><span></span><code><span class="kn">from</span><span class="w"> </span><span class="nn">scipy</span><span class="w"> </span><span class="kn">import</span> <span class="n">stats</span> <span class="c1"># Fit distribution to data</span> <span class="n">data</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="n">service_times</span><span class="p">)</span> <span class="c1"># Try several distributions</span> <span class="n">distributions</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'expon'</span><span class="p">,</span> <span class="s1">'gamma'</span><span class="p">,</span> <span class="s1">'lognorm'</span><span class="p">,</span> <span class="s1">'weibull_min'</span><span class="p">]</span> <span class="n">best_fit</span> <span class="o">=</span> <span class="kc">None</span> <span class="n">best_aic</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="s1">'inf'</span><span class="p">)</span> <span class="k">for</span> <span class="n">dist_name</span> <span class="ow">in</span> <span class="n">distributions</span><span class="p">:</span> <span class="n">dist</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">stats</span><span class="p">,</span> <span class="n">dist_name</span><span class="p">)</span> <span class="n">params</span> <span class="o">=</span> <span class="n">dist</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c1"># Kolmogorov-Smirnov test</span> <span class="n">ks_stat</span><span class="p">,</span> <span class="n">p_value</span> <span class="o">=</span> <span class="n">stats</span><span class="o">.</span><span class="n">kstest</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">dist_name</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="c1"># AIC</span> <span class="n">log_likelihood</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="n">dist</span><span class="o">.</span><span class="n">logpdf</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="o">*</span><span class="n">params</span><span class="p">))</span> <span class="n">k</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">params</span><span class="p">)</span> <span class="n">aic</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">k</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">log_likelihood</span> <span class="k">if</span> <span class="n">aic</span> <span class="o"><</span> <span class="n">best_aic</span><span class="p">:</span> <span class="n">best_aic</span> <span class="o">=</span> <span class="n">aic</span> <span class="n">best_fit</span> <span class="o">=</span> <span class="p">(</span><span class="n">dist_name</span><span class="p">,</span> <span class="n">params</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Best fit: </span><span class="si">{</span><span class="n">best_fit</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s2"> with params </span><span class="si">{</span><span class="n">best_fit</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span> </code></pre></div> <h2 id="vectorised-simulation-analysis">Vectorised Simulation Analysis</h2> <div class="code-block"><pre><span></span><code><span class="c1"># Instead of loops for statistics</span> <span class="n">wait_times</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="o">...</span><span class="p">])</span> <span class="n">service_times</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([</span><span class="o">...</span><span class="p">])</span> <span class="c1"># Vectorised calculations</span> <span class="n">total_times</span> <span class="o">=</span> <span class="n">wait_times</span> <span class="o">+</span> <span class="n">service_times</span> <span class="n">efficiency</span> <span class="o">=</span> <span class="n">service_times</span> <span class="o">/</span> <span class="n">total_times</span> <span class="n">above_threshold</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="n">wait_times</span> <span class="o">></span> <span class="mi">10</span><span class="p">)</span> <span class="c1"># Boolean indexing</span> <span class="n">long_waits</span> <span class="o">=</span> <span class="n">wait_times</span><span class="p">[</span><span class="n">wait_times</span> <span class="o">></span> <span class="mi">10</span><span class="p">]</span> </code></pre></div> <h2 id="common-random-numbers">Common Random Numbers</h2> <div class="code-block"><pre><span></span><code><span class="k">def</span><span class="w"> </span><span class="nf">run_with_crn</span><span class="p">(</span><span class="n">config_a</span><span class="p">,</span> <span class="n">config_b</span><span class="p">,</span> <span class="n">n_samples</span><span class="p">):</span> <span class="w"> </span><span class="sd">"""Compare scenarios using common random numbers."""</span> <span class="n">rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="c1"># Generate random numbers once</span> <span class="n">arrivals</span> <span class="o">=</span> <span class="n">rng</span><span class="o">.</span><span class="n">exponential</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">n_samples</span><span class="p">)</span> <span class="n">services</span> <span class="o">=</span> <span class="n">rng</span><span class="o">.</span><span class="n">lognormal</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="n">n_samples</span><span class="p">)</span> <span class="c1"># Run scenario A with these numbers</span> <span class="n">result_a</span> <span class="o">=</span> <span class="n">run_scenario</span><span class="p">(</span><span class="n">config_a</span><span class="p">,</span> <span class="n">arrivals</span><span class="p">,</span> <span class="n">services</span><span class="p">)</span> <span class="c1"># Run scenario B with same numbers</span> <span class="n">result_b</span> <span class="o">=</span> <span class="n">run_scenario</span><span class="p">(</span><span class="n">config_b</span><span class="p">,</span> <span class="n">arrivals</span><span class="p">,</span> <span class="n">services</span><span class="p">)</span> <span class="c1"># Paired comparison</span> <span class="n">differences</span> <span class="o">=</span> <span class="n">result_a</span> <span class="o">-</span> <span class="n">result_b</span> <span class="k">return</span> <span class="n">differences</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="n">differences</span><span class="o">.</span><span class="n">std</span><span class="p">()</span> </code></pre></div> <h2 id="complete-integration">Complete Integration</h2> <div class="code-block"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">simpy</span> <span class="kn">import</span><span class="w"> </span><span class="nn">numpy</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="nn">np</span> <span class="k">class</span><span class="w"> </span><span class="nc">NumpySimulation</span><span class="p">:</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">master_seed</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span> <span class="o">=</span> <span class="n">env</span> <span class="c1"># Random streams</span> <span class="n">master</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">master_seed</span><span class="p">)</span> <span class="n">seeds</span> <span class="o">=</span> <span class="n">master</span><span class="o">.</span><span class="n">integers</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">**</span><span class="mi">31</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">arrival_rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">seeds</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="bp">self</span><span class="o">.</span><span class="n">service_rng</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">default_rng</span><span class="p">(</span><span class="n">seeds</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="c1"># Resources</span> <span class="bp">self</span><span class="o">.</span><span class="n">server</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Resource</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">capacity</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># Results (will convert to numpy for analysis)</span> <span class="bp">self</span><span class="o">.</span><span class="n">wait_times</span> <span class="o">=</span> <span class="p">[]</span> <span class="bp">self</span><span class="o">.</span><span class="n">service_times</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">def</span><span class="w"> </span><span class="nf">customer</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">cid</span><span class="p">):</span> <span class="n">arrival</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="k">with</span> <span class="bp">self</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">request</span><span class="p">()</span> <span class="k">as</span> <span class="n">req</span><span class="p">:</span> <span class="k">yield</span> <span class="n">req</span> <span class="n">wait</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">now</span> <span class="o">-</span> <span class="n">arrival</span> <span class="n">service</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">service_rng</span><span class="o">.</span><span class="n">lognormal</span><span class="p">(</span><span class="mf">1.5</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">)</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="n">service</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">wait_times</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">wait</span><span class="p">)</span> <span class="bp">self</span><span class="o">.</span><span class="n">service_times</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">service</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">arrivals</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">cid</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span> <span class="k">yield</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">timeout</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">arrival_rng</span><span class="o">.</span><span class="n">exponential</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">customer</span><span class="p">(</span><span class="n">cid</span><span class="p">))</span> <span class="n">cid</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">def</span><span class="w"> </span><span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">duration</span><span class="p">):</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">arrivals</span><span class="p">())</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">until</span><span class="o">=</span><span class="n">duration</span><span class="p">)</span> <span class="k">def</span><span class="w"> </span><span class="nf">analyse</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="n">waits</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">wait_times</span><span class="p">)</span> <span class="n">services</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">service_times</span><span class="p">)</span> <span class="k">return</span> <span class="p">{</span> <span class="s1">'n_customers'</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">waits</span><span class="p">),</span> <span class="s1">'mean_wait'</span><span class="p">:</span> <span class="n">waits</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="s1">'std_wait'</span><span class="p">:</span> <span class="n">waits</span><span class="o">.</span><span class="n">std</span><span class="p">(),</span> <span class="s1">'median_wait'</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">median</span><span class="p">(</span><span class="n">waits</span><span class="p">),</span> <span class="s1">'p90_wait'</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">percentile</span><span class="p">(</span><span class="n">waits</span><span class="p">,</span> <span class="mi">90</span><span class="p">),</span> <span class="s1">'p95_wait'</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">percentile</span><span class="p">(</span><span class="n">waits</span><span class="p">,</span> <span class="mi">95</span><span class="p">),</span> <span class="s1">'max_wait'</span><span class="p">:</span> <span class="n">waits</span><span class="o">.</span><span class="n">max</span><span class="p">(),</span> <span class="s1">'mean_service'</span><span class="p">:</span> <span class="n">services</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="s1">'correlation'</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">corrcoef</span><span class="p">(</span><span class="n">waits</span><span class="p">,</span> <span class="n">services</span><span class="p">)[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span> <span class="p">}</span> <span class="c1"># Run</span> <span class="n">env</span> <span class="o">=</span> <span class="n">simpy</span><span class="o">.</span><span class="n">Environment</span><span class="p">()</span> <span class="n">sim</span> <span class="o">=</span> <span class="n">NumpySimulation</span><span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="n">master_seed</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span> <span class="n">sim</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="n">sim</span><span class="o">.</span><span class="n">analyse</span><span class="p">())</span> </code></pre></div> <h2 id="summary">Summary</h2> <p>NumPy for SimPy: - Modern random API with <code>default_rng()</code> - More distributions than <code>random</code> - Separate streams for reproducibility - Fast array operations for analysis - Vectorised calculations</p> <p>Better randomness. Faster analysis.</p> <h2 id="next-steps">Next Steps</h2> <ul> <li><a href="/blog_posts/simpy-random-numbers.html">Random Numbers in SimPy</a></li> <li><a href="/blog_posts/simpy-with-pandas.html">SimPy with pandas</a></li> <li><a href="/blog_posts/simpy-service-times.html">Service Time Distributions</a></li> </ul>Discover the Power of Simulation
Want to become a go-to expert in simulation with Python? The Complete Simulation Bootcamp will show you how simulation can transform your career and your projects.
Explore the Bootcamp