<?xml version="1.0" encoding="utf-8"?> 
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us">
    <generator uri="https://gohugo.io/" version="0.152.2">Hugo</generator><title type="html"><![CDATA[Tutorial on Blog]]></title>
    
    
    
            <link href="https://blog.scientific-python.org/tags/tutorial/" rel="alternate" type="text/html" title="html" />
            <link href="https://blog.scientific-python.org/tags/tutorial/atom.xml" rel="self" type="application/atom" title="atom" />
    <updated>2026-04-04T04:32:36+00:00</updated>
    
    
    
    
        <id>https://blog.scientific-python.org/tags/tutorial/</id>
    
        
        <entry>
            <title type="html"><![CDATA[A quick tour of QMC with SciPy]]></title>
            <link href="https://blog.scientific-python.org/scipy/qmc-basics/?utm_source=atom_feed" rel="alternate" type="text/html" />
            
            
                <id>https://blog.scientific-python.org/scipy/qmc-basics/</id>
            
            
            <published>2022-04-04T00:00:00+00:00</published>
            <updated>2022-04-04T00:00:00+00:00</updated>
            
            
            <content type="html"><![CDATA[<blockquote>Do you need to use random numbers? Use Quasi-Monte Carlo (QMC) methods instead. QMC, what is it? Why you should care? And how to use it?</blockquote><p>At the end of this article, my goal is to convince you that: if you need to
use random numbers, you <em>should</em> consider using
<a href="https://scipy.github.io/devdocs/reference/stats.qmc.html"><code>scipy.stats.qmc</code></a>
instead of
<a href="https://numpy.org/doc/stable/reference/random/index.html"><code>np.random</code></a>.</p>
<p>In the following, we assume that <em>SciPy</em>, <em>NumPy</em> and <em>Matplotlib</em> are
installed and imported:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">qmc</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span></span></span></code></pre>
</div>
<blockquote>
<p>Note that no seeding is used in these examples. This will be the topic
of another article: seeding should <strong>only</strong> be used for testing purposes.</p>
</blockquote>
<p><strong>So what are Monte Carlo (MC) and Quasi-Monte Carlo (QMC)?</strong></p>
<h2 id="monte-carlo-mc">Monte Carlo (MC)<a class="headerlink" href="#monte-carlo-mc" title="Link to this heading">#</a></h2>
<blockquote>
<p>MC methods are a broad class of computational algorithms that rely on
repeated random sampling to obtain numerical results.
The underlying concept is to use randomness to solve problems that might be
deterministic in principle. They are often used in physical and mathematical
problems and are most useful when it is difficult or impossible to use other
approaches. MC methods are mainly used in three classes of problem:
optimization, numerical integration, and generating draws from a probability
distribution.</p>
</blockquote>
<p>Put simply, this is how you would usually generate a <em>sample</em> of points using
MC:</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><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></span><span class="line"><span class="cl"><span class="n">sample</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">size</span><span class="o">=</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span></span></span></code></pre>
</div>
<p>In this case, <code>sample</code> is a 2-dimensional array with 256 points which can be
visualized using a 2D scatter plot.</p>
<p><img src="/scipy/qmc-basics/mc.png" alt="MC sample using a 2D scatter plot."></p>
<p>In the plot above, points are generated randomly without any
knowledge about previously drawn points. It is clear that some regions of the
space are left unexplored while other regions have clusters. In an optimization
problem, it could mean that you would need to generate more sample to find the
optimum. Or in a regression problem, you could also overfit a model due to
some cluster of points.</p>
<p>Generating random numbers is a more complex problem than it sounds. Simple MC
methods are designed to sample points to be independent and identically
distributed (IID).</p>
<p>One could think that the solution is just to use a grid! But look what
happens if we have a distance of 0.1 between points in the unit-hypercube (
with all bounds ranging from 0 to 1).</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">disc</span> <span class="o">=</span> <span class="mi">10</span>
</span></span><span class="line"><span class="cl"><span class="n">x1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linspace</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="n">disc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">x2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linspace</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="n">disc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">x3</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linspace</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="n">disc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">x1</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="n">x3</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">meshgrid</span><span class="p">(</span><span class="n">x1</span><span class="p">,</span> <span class="n">x2</span><span class="p">,</span> <span class="n">x3</span><span class="p">)</span></span></span></code></pre>
</div>
<p>The number of points required to fill the unit interval would be 10.
In a 2-dimensional hypercube the same spacing would require 100, and in 3
dimensions 1,000 points. As the number of dimensions grows, the number of
samples which is required to fill the space rises exponentially as the
dimensionality of the space increases. This exponential growth is called
the <em>curse of dimensionality</em>.</p>
<p><img src="/scipy/qmc-basics/curse.png" alt="Curse of dimensionality. 3 figures: 1D, 2D and 3D scatter plots of samples."></p>
<h2 id="quasi-monte-carlo-qmc">Quasi-Monte Carlo (QMC)<a class="headerlink" href="#quasi-monte-carlo-qmc" title="Link to this heading">#</a></h2>
<p>To mitigate the <em>curse of dimensionality</em>, you could decide to randomly
remove points from the sample or randomly sample in n-dimension. In both
cases, this <strong>will</strong> need to empty regions and clusters of points elsewhere.</p>
<p>Quasi-Monte Carlo (QMC) methods have been created specifically to answer this
problem. As opposed to MC methods, QMC methods are deterministic. Which means
that the points are not IID, but each new point knows about previous points.
The result is that we can construct samples with good coverage of the space.</p>
<blockquote>
<p>Deterministic does <strong>not</strong> mean that samples are always the same.
the sequences can be scrambled.</p>
</blockquote>
<p>Starting with version 1.7, SciPy provides QMC methods in
<a href="https://scipy.github.io/devdocs/reference/stats.qmc.html"><code>scipy.stats.qmc</code></a>.</p>
<p>Let&rsquo;s generate 2 samples with MC and a QMC method named <em>Sobol&rsquo;</em>.</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">n</span><span class="p">,</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">256</span><span class="p">,</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><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></span><span class="line"><span class="cl"><span class="n">sample_mc</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">size</span><span class="o">=</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">d</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">qrng</span> <span class="o">=</span> <span class="n">qmc</span><span class="o">.</span><span class="n">Sobol</span><span class="p">(</span><span class="n">d</span><span class="o">=</span><span class="n">d</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">sample_qmc</span> <span class="o">=</span> <span class="n">qrng</span><span class="o">.</span><span class="n">random</span><span class="p">(</span><span class="n">n</span><span class="o">=</span><span class="n">n</span><span class="p">)</span></span></span></code></pre>
</div>
<p>A very similar interface, but as seen below, with radically different results.</p>
<p><img src="/scipy/qmc-basics/mc_sobol.png" alt="Comparison between MC and QMC samples using a 2D scatter plot."></p>
<p>The 2D space clearly exhibit less empty areas and less clusters with the QMC
sample.</p>
<h2 id="quality">Quality?<a class="headerlink" href="#quality" title="Link to this heading">#</a></h2>
<p>Beyond the visual improvement of <em>quality</em>, there are metrics to assess the
quality of a sample. Geometrical criteria are commonly used, one can
compute the distance (L1, L2, etc.) between all pairs of points. But there
are also statistical criteria such as: the
<a href="https://scipy.github.io/devdocs/reference/generated/scipy.stats.qmc.discrepancy.html">discrepancy</a>.</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">qmc</span><span class="o">.</span><span class="n">discrepancy</span><span class="p">(</span><span class="n">sample_mc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 0.0009</span>
</span></span><span class="line"><span class="cl"><span class="n">qmc</span><span class="o">.</span><span class="n">discrepancy</span><span class="p">(</span><span class="n">sample_qmc</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 1.1e-05</span></span></span></code></pre>
</div>
<p>The lower the value, the better the quality.</p>
<h2 id="convergence">Convergence<a class="headerlink" href="#convergence" title="Link to this heading">#</a></h2>
<p>If this still does not convince you, let&rsquo;s look at a concrete example:
integrating a function. Let&rsquo;s look at the mean of the squared sum in
5 dimensions:</p>
<p>$$f(\mathbf{x}) = \left( \sum_{j=1}^{5}x_j \right)^2,$$</p>
<p>with $x_j \sim \mathcal{U}(0,1)$. It has a known mean value,
$\mu = 5/3+5(5-1)/4$. By sampling points, we can compute that mean numerically.</p>
<blockquote>
<p>The samplings are done 99 times and averaged. The variance is not reported
for simplicity, just know that it&rsquo;s guaranteed to be lower with QMC than with
MC.</p>
</blockquote>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">dim</span> <span class="o">=</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl"><span class="n">ref</span> <span class="o">=</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">5</span> <span class="o">*</span> <span class="p">(</span><span class="mi">5</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4</span>
</span></span><span class="line"><span class="cl"><span class="n">n_conv</span> <span class="o">=</span> <span class="mi">99</span>
</span></span><span class="line"><span class="cl"><span class="n">ns_gen</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">**</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">13</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">sample</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># dim 5, true value 5/3 + 5*(5 - 1)/4</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">np</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="n">sample</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="o">**</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">conv_method</span><span class="p">(</span><span class="n">sampler</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">n_samples</span><span class="p">,</span> <span class="n">n_conv</span><span class="p">,</span> <span class="n">ref</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">samples</span> <span class="o">=</span> <span class="p">[</span><span class="n">sampler</span><span class="p">(</span><span class="n">n_samples</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n_conv</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">    <span class="n">samples</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">samples</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">evals</span> <span class="o">=</span> <span class="p">[</span><span class="n">np</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="n">func</span><span class="p">(</span><span class="n">sample</span><span class="p">))</span> <span class="o">/</span> <span class="n">n_samples</span> <span class="k">for</span> <span class="n">sample</span> <span class="ow">in</span> <span class="n">samples</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="n">squared_errors</span> <span class="o">=</span> <span class="p">(</span><span class="n">ref</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">evals</span><span class="p">))</span> <span class="o">**</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">    <span class="n">rmse</span> <span class="o">=</span> <span class="p">(</span><span class="n">np</span><span class="o">.</span><span class="n">sum</span><span class="p">(</span><span class="n">squared_errors</span><span class="p">)</span> <span class="o">/</span> <span class="n">n_conv</span><span class="p">)</span> <span class="o">**</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">rmse</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Analysis</span>
</span></span><span class="line"><span class="cl"><span class="n">sample_mc_rmse</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="n">sample_sobol_rmse</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl"><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></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">ns</span> <span class="ow">in</span> <span class="n">ns_gen</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Monte Carlo</span>
</span></span><span class="line"><span class="cl">    <span class="n">sampler_mc</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">rng</span><span class="o">.</span><span class="n">random</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">dim</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">conv_res</span> <span class="o">=</span> <span class="n">conv_method</span><span class="p">(</span><span class="n">sampler_mc</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">ns</span><span class="p">,</span> <span class="n">n_conv</span><span class="p">,</span> <span class="n">ref</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sample_mc_rmse</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">conv_res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Sobol&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">engine</span> <span class="o">=</span> <span class="n">qmc</span><span class="o">.</span><span class="n">Sobol</span><span class="p">(</span><span class="n">d</span><span class="o">=</span><span class="n">dim</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">conv_res</span> <span class="o">=</span> <span class="n">conv_method</span><span class="p">(</span><span class="n">engine</span><span class="o">.</span><span class="n">random</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="n">ns</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ref</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">sample_sobol_rmse</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">conv_res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">sample_mc_rmse</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">sample_mc_rmse</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">sample_sobol_rmse</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">sample_sobol_rmse</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Plot</span>
</span></span><span class="line"><span class="cl"><span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">set_aspect</span><span class="p">(</span><span class="s2">&#34;equal&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># MC</span>
</span></span><span class="line"><span class="cl"><span class="n">ratio</span> <span class="o">=</span> <span class="n">sample_mc_rmse</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">/</span> <span class="n">ns_gen</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">**</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">ns_gen</span><span class="p">,</span> <span class="n">ns_gen</span> <span class="o">**</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">ratio</span><span class="p">,</span> <span class="n">ls</span><span class="o">=</span><span class="s2">&#34;-&#34;</span><span class="p">,</span> <span class="n">c</span><span class="o">=</span><span class="s2">&#34;k&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">scatter</span><span class="p">(</span><span class="n">ns_gen</span><span class="p">,</span> <span class="n">sample_mc_rmse</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s2">&#34;MC: np.random&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Sobol&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">ratio</span> <span class="o">=</span> <span class="n">sample_sobol_rmse</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">/</span> <span class="n">ns_gen</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">**</span> <span class="p">(</span><span class="o">-</span><span class="mi">2</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">ns_gen</span><span class="p">,</span> <span class="n">ns_gen</span> <span class="o">**</span> <span class="p">(</span><span class="o">-</span><span class="mi">2</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="n">ratio</span><span class="p">,</span> <span class="n">ls</span><span class="o">=</span><span class="s2">&#34;-&#34;</span><span class="p">,</span> <span class="n">c</span><span class="o">=</span><span class="s2">&#34;k&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">scatter</span><span class="p">(</span><span class="n">ns_gen</span><span class="p">,</span> <span class="n">sample_sobol_rmse</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s2">&#34;QMC: qmc.Sobol&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="sa">r</span><span class="s2">&#34;$N_s$&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">set_xscale</span><span class="p">(</span><span class="s2">&#34;log&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">set_xticks</span><span class="p">(</span><span class="n">ns_gen</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">set_xticklabels</span><span class="p">([</span><span class="sa">rf</span><span class="s2">&#34;$2^</span><span class="se">{{</span><span class="si">{</span><span class="n">ns</span><span class="si">}</span><span class="se">}}</span><span class="s2">$&#34;</span> <span class="k">for</span> <span class="n">ns</span> <span class="ow">in</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">13</span><span class="p">)])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="sa">r</span><span class="s2">&#34;$\log (\epsilon)$&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">set_yscale</span><span class="p">(</span><span class="s2">&#34;log&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">ax</span><span class="o">.</span><span class="n">legend</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="s2">&#34;upper right&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">fig</span><span class="o">.</span><span class="n">tight_layout</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span></span></span></code></pre>
</div>
<p><img src="/scipy/qmc-basics/conv_mc_sobol.png" alt="Convergence of the integration error with MC and QMC."></p>
<p>With MC the approximation error follows a theoretical rate of $O(n^{-1/2})$.
But, QMC methods have better rates of convergence and achieve $O(n^{-1})$
for this function–and even better rates on very smooth functions.</p>
<p>This means that using $2^8=256$ points from <em>Sobol&rsquo;</em> leads to a lower
error than using $2^{12}=4096$ points from MC! When the function evaluation
is costly, it can bring huge computational savings.</p>
<h2 id="sampling-from-any-distribution-advanced">Sampling from any distribution (advanced)<a class="headerlink" href="#sampling-from-any-distribution-advanced" title="Link to this heading">#</a></h2>
<p>But there is more! Another great use of QMC is to sample arbitrary
distributions. In SciPy 1.8, there are new classes of
<a href="https://scipy.github.io/devdocs/reference/stats.sampling.html">samplers</a>
that allow you to sample from any custom distribution. And some of
these methods can use QMC with a <code>qrvs</code> method:</p>
<ul>
<li><a href="https://scipy.github.io/devdocs/reference/generated/scipy.stats.sampling.NumericalInversePolynomial.html">NumericalInversePolynomial</a></li>
<li><a href="https://scipy.github.io/devdocs/reference/generated/scipy.stats.sampling.NumericalInverseHermite.html">NumericalInverseHermite</a></li>
</ul>
<p>Here is an example with a distribution from SciPy: <em>fisk</em>. We generate
a MC sample from the distribution (either directly from the distribution with
<code>fisk.rvs</code> or using <code>NumericalInverseHermite.rvs</code>) and another sample with
QMC using <code>NumericalInverseHermite.qrvs</code>.</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">scipy.stats</span> <span class="k">as</span> <span class="nn">stats</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">sampling</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Any distribution</span>
</span></span><span class="line"><span class="cl"><span class="n">c</span> <span class="o">=</span> <span class="mf">3.9</span>
</span></span><span class="line"><span class="cl"><span class="n">dist</span> <span class="o">=</span> <span class="n">stats</span><span class="o">.</span><span class="n">fisk</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># MC</span>
</span></span><span class="line"><span class="cl"><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></span><span class="line"><span class="cl"><span class="n">sample_mc</span> <span class="o">=</span> <span class="n">dist</span><span class="o">.</span><span class="n">rvs</span><span class="p">(</span><span class="mi">128</span><span class="p">,</span> <span class="n">random_state</span><span class="o">=</span><span class="n">rng</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># QMC</span>
</span></span><span class="line"><span class="cl"><span class="n">rng_dist</span> <span class="o">=</span> <span class="n">sampling</span><span class="o">.</span><span class="n">NumericalInverseHermite</span><span class="p">(</span><span class="n">dist</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># sample_mc = rng_dist.rvs(128, random_state=rng)  # MC alternative same as above</span>
</span></span><span class="line"><span class="cl"><span class="n">qrng</span> <span class="o">=</span> <span class="n">qmc</span><span class="o">.</span><span class="n">Sobol</span><span class="p">(</span><span class="n">d</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">sample_qmc</span> <span class="o">=</span> <span class="n">rng_dist</span><span class="o">.</span><span class="n">qrvs</span><span class="p">(</span><span class="mi">128</span><span class="p">,</span> <span class="n">qmc_engine</span><span class="o">=</span><span class="n">qrng</span><span class="p">)</span></span></span></code></pre>
</div>
<p>Let&rsquo;s visualize the difference between MC and QMC by calculating the empirical
Probability Density Function (PDF). The QMC results are clearly superior
to MC.</p>


<div class="highlight">
  <pre class="chroma"><code><span class="line"><span class="cl"><span class="c1"># Visualization</span>
</span></span><span class="line"><span class="cl"><span class="n">fig</span><span class="p">,</span> <span class="n">axs</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="n">sharey</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">sharex</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">x</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">linspace</span><span class="p">(</span><span class="n">dist</span><span class="o">.</span><span class="n">ppf</span><span class="p">(</span><span class="mf">0.01</span><span class="p">),</span> <span class="n">dist</span><span class="o">.</span><span class="n">ppf</span><span class="p">(</span><span class="mf">0.99</span><span class="p">),</span> <span class="mi">100</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">pdf</span> <span class="o">=</span> <span class="n">dist</span><span class="o">.</span><span class="n">pdf</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">delta</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">max</span><span class="p">(</span><span class="n">pdf</span><span class="p">)</span> <span class="o">*</span> <span class="mf">5e-2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">samples</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;MC: np.random&#34;</span><span class="p">:</span> <span class="n">sample_mc</span><span class="p">,</span> <span class="s2">&#34;QMC: qmc.Sobol&#34;</span><span class="p">:</span> <span class="n">sample_qmc</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">ax</span><span class="p">,</span> <span class="n">sample</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">axs</span><span class="p">,</span> <span class="n">samples</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="n">sample</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">pdf</span><span class="p">,</span> <span class="s2">&#34;-&#34;</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s2">&#34;fisk PDF&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">samples</span><span class="p">[</span><span class="n">sample</span><span class="p">],</span> <span class="o">-</span><span class="n">delta</span> <span class="o">-</span> <span class="n">delta</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">random</span><span class="p">(</span><span class="mi">128</span><span class="p">),</span> <span class="s2">&#34;+k&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">kde</span> <span class="o">=</span> <span class="n">stats</span><span class="o">.</span><span class="n">gaussian_kde</span><span class="p">(</span><span class="n">samples</span><span class="p">[</span><span class="n">sample</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">kde</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="s2">&#34;-.&#34;</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s2">&#34;empirical PDF&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># or use a histogram</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ax.hist(sample, density=True, histtype=&#39;stepfilled&#39;, alpha=0.2)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ax</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">legend</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="s2">&#34;best&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">fig</span><span class="o">.</span><span class="n">supylabel</span><span class="p">(</span><span class="s2">&#34;Density&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">fig</span><span class="o">.</span><span class="n">supxlabel</span><span class="p">(</span><span class="s2">&#34;Sample value&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">fig</span><span class="o">.</span><span class="n">tight_layout</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span></span></span></code></pre>
</div>
<p><img src="/scipy/qmc-basics/fisk_mc_sobol.png" alt="Probability density function of the fisk distribution.
Comparison with empirical distributions built with MC and QMC."></p>
<p>Careful readers will note that there is no seeding. This is intentional as
noted at the beginning of this article. You might run this code
again and have better results with MC. <strong>But</strong> only sometimes. And that&rsquo;s
exactly my point. On average, you are guaranteed to have more consistent
results with a better quality using QMC. I invite you to try it and see for
yourself!</p>
<h2 id="conclusion">Conclusion<a class="headerlink" href="#conclusion" title="Link to this heading">#</a></h2>
<p>I hope that I convinced you to use QMC the next time you need random numbers.
QMC is superior to MC, period.</p>
<p>There is an extensive body of literature and rigorous proofs. One reason MC is
still more popular is that QMC is harder to implement and, depending on the
method, there are rules to follow.</p>
<p>Take the <em>Sobol&rsquo;</em> method we used: you must use exactly $2^n$ sample. If you
don&rsquo;t do it, you will break some properties and end up having the same
performance than MC. This is why some people argue that QMC is not better:
they simply don&rsquo;t use the methods properly, hence fail to see any benefits and
conclude that MC is &ldquo;enough&rdquo;.</p>
<p>In
<a href="https://scipy.github.io/devdocs/reference/stats.qmc.html"><code>scipy.stats.qmc</code></a>,
we went to great lengths to explain how to use the methods, and we added some
explicit warnings to make the methods accessible and useful to
everyone.</p>
]]></content>
            
                 
                    
                 
                    
                         
                        
                            
                             
                                <category scheme="taxonomy:Tags" term="scipy" label="scipy" />
                             
                                <category scheme="taxonomy:Tags" term="tutorial" label="tutorial" />
                             
                                <category scheme="taxonomy:Tags" term="random-numbers" label="random-numbers" />
                            
                        
                    
                
            
        </entry>
    
</feed>
