<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://afmagee.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://afmagee.github.io/" rel="alternate" type="text/html" /><updated>2026-07-14T14:39:44+00:00</updated><id>https://afmagee.github.io/feed.xml</id><title type="html">@afmagee</title><subtitle>My personal website.</subtitle><author><name>Andrew Magee</name></author><entry><title type="html">The Fréchet PSRF, part 3</title><link href="https://afmagee.github.io/frechet-psrf-3/" rel="alternate" type="text/html" title="The Fréchet PSRF, part 3" /><published>2026-07-14T00:00:00+00:00</published><updated>2026-07-14T00:00:00+00:00</updated><id>https://afmagee.github.io/frechet-psrf-3</id><content type="html" xml:base="https://afmagee.github.io/frechet-psrf-3/"><![CDATA[<p><a href="https://afmagee.github.io/frechet-psrf/">Previously</a>, I attempted to derive a Fréchet generalization of the PSRF.
<a href="https://afmagee.github.io/frechet-psrf-2/">Then</a> I stumbled around in the dark and stumbled on a form that checks out.
Here I’m going to actually do the math I should have done in the first place.</p>

<h2 id="what-i-am-and-am-not-going-to-do-here">What I am and am not going to do here</h2>

<p>All I’m going to do here is fix the $B$ term in the Fréchet PSRF.
I’ll then verify it, and look at where I went wrong previously.
I’ll stitch this all back together separately to present the entire Fréchet PSRF, and only then compare this all to the Gelman-Rubin-like diagnostic of Whidden and Matsen (2015).</p>

<h2 id="the-key-quantity">The key quantity</h2>

<p>We have RVs $\Xi$ and $\Upsilon$, and for each we have $n$ samples $\xi_1, \dots, \xi_n$ and $\upsilon_1, \dots, \upsilon_n$.
These have sample means $\bar{\xi}$ and $\bar{\upsilon}$.
They have (Bessel-corrected) sample variances $s_\xi^2$ and $s_\upsilon^2$.
They have (Bessel-corrected) sample covariance $S_{\xi \upsilon}$.</p>

<p>We want to know what this term is equal to.</p>

\[\frac{1}{n^2} \sum_i \sum_j (\xi_i - \upsilon_j)^2\]

<p>So, now we do the math.
More or less one term at a time.
And yes, this time I’m cheating a little because I’ve more or less worked it out already and I want the math to look slightly less horrific.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>
The shape we’re aiming for here is a decomposition into two variance terms and a term about the difference in means (we’d accept a covariance term in the mix, but we’ll be assuming independence so it will go away).
Most of the math amounts to either finding the terms that you can push outside a sum, or converting between means (times constants) and sums.</p>

<p>I’m going to try to preserve the larger features of what we’re doing by relegating some of the “everything ends up canceling” type detours to subsections.
I’m using gratuitous bracketing to try to keep sum terms clear and to make sure it’s clear the difference between <code class="language-plaintext highlighter-rouge">\bar{\xi}^2</code> and <code class="language-plaintext highlighter-rouge">\bar{\xi^2}</code>.
(Though we use only the former.)</p>

\[\frac{1}{n^2} \sum_i \sum_j \left[ \xi_i^2 - 2\xi_i \upsilon_j + \upsilon_j^2 \right]\]

\[\frac{1}{n} \sum_i \left[ \xi_i^2 \right]
+ \frac{1}{n^2} \sum_i \sum_j \left[ 2\xi_i \upsilon_j \right]
+ \frac{1}{n} \sum_j \left[ \upsilon_j^2 \right]\]

<p>By <a href="#detour-1">Detour 1</a>, the left- and right-most terms here yield,</p>

\[\left[ \frac{n - 1}{n} s_\xi^2  + (\bar{\xi})^2 \right]
- \frac{1}{n^2} \sum_i \sum_j \left[ 2\xi_i \upsilon_j \right]
+ \left[ \frac{n - 1}{n} s_\upsilon^2  + (\bar{\upsilon})^2 \right]\]

<p>By <a href="#detour-2">Detour 2</a>, under the assumption that $\Xi$ and $\Upsilon$ are independent,</p>

\[\left[ \frac{n - 1}{n} s_\xi^2  + (\bar{\xi})^2 \right]
- \left[ 2 \bar{\upsilon} \bar{\xi} \right]
+ \left[ \frac{n - 1}{n} s_\upsilon^2  + (\bar{\upsilon})^2 \right]\]

<p>Now we clean up and recognize that we’ve got a quadratic sitting around,</p>

\[\frac{n - 1}{n} s_\xi^2 + \frac{n - 1}{n} s_\upsilon^2
- 2 \bar{\upsilon} \bar{\xi}
+ (\bar{\xi})^2 + (\bar{\upsilon})^2\]

\[\frac{n - 1}{n} s_\xi^2 + \frac{n - 1}{n} s_\upsilon^2
+ (\bar{\xi} - \bar{\upsilon})^2\]

<h2 id="getting-it-right-finally">Getting it right, finally</h2>

<p>We have now established that,</p>

\[\frac{1}{n^2} \sum_i \sum_j (\xi_i - \upsilon_j)^2 =
\frac{n - 1}{n} s_\xi^2 + \frac{n - 1}{n} s_\upsilon^2
+ (\bar{\xi} - \bar{\upsilon})^2\]

<p>For the PSRF, we want the squared difference in means, so we rearrange it,</p>

\[(\bar{\xi} - \bar{\upsilon})^2 = 
\frac{1}{n^2} \sum_i \sum_j (\xi_i - \upsilon_j)^2
- \frac{n - 1}{n} s_\xi^2 - \frac{n - 1}{n} s_\upsilon^2\]

<p>The “usual” $B_{\mathrm{F}}$ generalization is then,</p>

\[d(\bar{\xi}, \bar{\upsilon})^2 = 
\frac{1}{n^2} \sum_i \sum_j d(\xi_i, \upsilon_j)^2 -
\frac{n - 1}{n} s_\xi^2 - \frac{n - 1}{n} s_\upsilon^2\]

<p>At this point I hesitate to invoke a new name for the right-most term here, but for independent sets of samples I do think we can call it the (sample) average squared distance between the samples.</p>

<p>Recall that the definition of the Fréchet sample variance is</p>

\[s_{\text{F}}^2 = \frac{1}{n (n - 1)} \sum_{j &gt; i} d(\xi_i, \xi_j)^2\]

<p>This means we can write out all the terms as sums over parts of matrices as,</p>

\[d(\bar{\xi}, \bar{\upsilon})^2 = 
\frac{1}{n^2} \sum_i \sum_j d(\xi_i, \upsilon_j)^2
- \frac{1}{n^2} \sum_{j &gt; i} d(\xi_i, \xi_j)^2
- \frac{1}{n^2} \sum_{j &gt; i} d(\upsilon_i, \upsilon_j)^2\]

<h2 id="b_mathrmf">$B_{\mathrm{F}}$</h2>

<p>To get the $B_{\mathrm{F}}$ term in the Fréchet PSRF, now we move from $\Xi$ and $\Upsilon$ to MCMC replicates ${X}_{k \cdot}$ and ${X}_{\ell \cdot}$.</p>

\[d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 = 
\frac{1}{n^2} \sum_i \sum_j d(x_{k i}, x_{\ell j})^2
- \frac{1}{n^2} \sum_{j &gt; i} d(x_{k i}, x_{k j})^2
- \frac{1}{n^2} \sum_{j &gt; i} d(x_{\ell i}, x_{\ell j})^2\]

<p>And <em>this</em> is suitable for plugging directly into what we’d otherwise expect the Fréchet PSRF’s $B_{\mathrm{F}}$ term should look like, without any funky counter-correction terms,</p>

\[\frac{B_{\mathrm{F}}}{n} = \frac{1}{m (m - 1)} \sum_{\ell &gt; k} d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<h2 id="so-what-did-i-do-wrong">So, what did I do wrong?</h2>

<p>The only lesson I’m sure I can extract here is that I should have just done the math for the terms I was actually using.</p>

<p><a href="https://afmagee.github.io/frechet-psrf/">The first time out, I went</a>, I started with an identity I had <a href="https://projecteuclid.org/journals/bayesian-analysis/volume-19/issue-2/How-Trustworthy-Is-Your-Tree-Bayesian-Phylogenetic-Effective-Sample-Size/10.1214/22-BA1339.full">written out previously</a>.
I then looked at the terms, figured I knew what they meant, and plugged values in.
I can’t quite tell if I got mucked up in <em>population</em> vs <em>sample</em> thinking,<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> or if I didn’t quite get the unbiased estimator I was trying for (by dint of trying to match unbiased variance estimators in the usual PSRF equations).
Partly this is because I’m still not quite sure what the mean squared difference terms really are.</p>

<p>My <a href="https://afmagee.github.io/frechet-psrf-2/">stumbling around in the dark</a> shows the danger of intuition.
I was in the right wheelhouse of the solution, because my original term was a bit too small.
But I started adding extra terms in and over-corrected, so I ended up having to re-correct the overall $B$ by an odd amount.</p>

<p>All in all, though, most of this isn’t the most <em>importantly</em> wrong.
My bad estimator in the first post was close enough to right to get something that would be a recognizable estimator of the PSRF.
But it’s safer to operate with firm ground under one’s feet, so it’s good to fix these things.</p>

<h2 id="mathematical-detours">Mathematical detours</h2>

<p>Feel free to treat this like an appendix and ignore it.</p>

<h3 id="detour-1">Detour 1</h3>

\[\frac{1}{n} \sum_i \left[ \xi_i^2 \right]\]

\[\frac{1}{n} \sum_i \left[ (\xi_i - \bar{\xi})^2  - (\bar{\xi})^2  + 2 \xi_i \bar{\xi} \right]\]

\[\frac{1}{n} \sum_i \left[ (\xi_i - \bar{\xi})^2 \right]  - \frac{1}{n} \sum_i \left[ (\bar{\xi})^2  \right]  + \frac{1}{n} \sum_i \left[  2 \xi_i \bar{\xi} \right]\]

\[\frac{1}{n} \sum_i \left[ (\xi_i - \bar{\xi})^2 \right]  - \frac{1}{n} \sum_i \left[ (\bar{\xi})^2  \right]  + \frac{2 \bar{\xi}}{n} \sum_i \left[ \xi_i \right]\]

\[\frac{1}{n} \sum_i \left[ (\xi_i - \bar{\xi})^2 \right]  - \frac{1}{n} \sum_i \left[ (\bar{\xi})^2  \right]  + 2 (\bar{\xi} )^2\]

\[\frac{1}{n} \sum_i \left[ (\xi_i - \bar{\xi})^2 \right]  - (\bar{\xi})^2  + 2 (\bar{\xi} )^2\]

\[\frac{1}{n} \sum_i \left[ (\xi_i - \bar{\xi})^2 \right]  + (\bar{\xi})^2\]

\[\frac{n - 1}{n} s_\xi^2  + (\bar{\xi})^2\]

<h3 id="detour-2">Detour 2</h3>

<p>Some day I may come back and work out what this is without starting from the assumption of independence.
But since we’re trying to work out multi-chain convergence diagnostics, it’s a huge time saver just to plug that in.
Since we have <em>independent</em> sample sets, $\xi_i$ is independent of $\upsilon_j$, and we can push the sums and averages around.</p>

\[\frac{1}{n^2} \sum_i \sum_j \left[ 2\xi_i \upsilon_j \right]\]

\[\frac{2}{n^2} \sum_i \sum_j \xi_i \upsilon_j\]

\[\frac{2}{n^2} \sum_i \xi_i \sum_j \upsilon_j\]

\[\frac{2}{n^2} \sum_i \xi_i \left[ n \bar{\upsilon} \right]\]

\[\frac{2 \bar{\upsilon}}{n} \sum_i \xi_i\]

\[\frac{2 \bar{\upsilon}}{n} \left[ n \bar{\xi} \right]\]

\[2 \bar{\upsilon} \bar{\xi}\]

<h2 id="code-appendix">Code appendix</h2>

<p>As I keep mucking up the math, it proves useful to spot-check that I get it right.</p>

<h3 id="the-fix">The fix</h3>

<p>This checks the key identity.
(Small sample sizes are better for seeing small off by 1 or (n - 1)/n type errors).</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">check_it</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
    </span><span class="n">stopifnot</span><span class="p">(</span><span class="nf">length</span><span class="p">(</span><span class="n">y</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="n">ones</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">

    </span><span class="n">lhs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w">
        </span><span class="nf">sum</span><span class="p">(</span><span class="n">sapply</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">xi</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="n">sapply</span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">yj</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="p">(</span><span class="n">xi</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">yj</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="w">
            </span><span class="p">})</span><span class="w">
        </span><span class="p">}))</span><span class="w">

    </span><span class="n">rhs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">var</span><span class="p">(</span><span class="n">x</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
        </span><span class="p">((</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">var</span><span class="p">(</span><span class="n">y</span><span class="p">))</span><span class="w"> </span><span class="o">+</span><span class="w">
        </span><span class="p">(</span><span class="n">mean</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">y</span><span class="p">))</span><span class="o">^</span><span class="m">2</span><span class="w">

    </span><span class="nf">return</span><span class="p">(</span><span class="n">lhs</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">rhs</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">nrep</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1000</span><span class="w">
</span><span class="n">nsamp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">10</span><span class="w">

</span><span class="n">res</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="nf">seq_len</span><span class="p">(</span><span class="n">nrep</span><span class="p">),</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rnorm</span><span class="p">(</span><span class="n">nsamp</span><span class="p">)</span><span class="w">
    </span><span class="n">y</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">rexp</span><span class="p">(</span><span class="n">nsamp</span><span class="p">)</span><span class="w">
    </span><span class="n">check_it</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w">
</span><span class="p">})</span><span class="w">

</span><span class="n">summary</span><span class="p">(</span><span class="n">res</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<h3 id="the-whole-enchilada">The whole enchilada</h3>

<p>This code checks the math for the entire B term.</p>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">set.seed</span><span class="p">(</span><span class="m">42</span><span class="p">)</span><span class="w">

</span><span class="n">nrep</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">
</span><span class="n">nsamp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">

</span><span class="n">b_classic</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w">

  </span><span class="n">stopifnot</span><span class="p">(</span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"list"</span><span class="p">,</span><span class="w"> </span><span class="nf">all</span><span class="p">(</span><span class="n">lengths</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w">

  </span><span class="n">chain_means</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">mean</span><span class="p">)</span><span class="w">
  </span><span class="n">grand_mean</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">chain_means</span><span class="p">)</span><span class="w">

  </span><span class="n">n</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">sum</span><span class="p">((</span><span class="n">chain_means</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">grand_mean</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">b_frechet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w">

  </span><span class="n">stopifnot</span><span class="p">(</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"list"</span><span class="p">,</span><span class="w">
    </span><span class="nf">all</span><span class="p">(</span><span class="n">lengths</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">),</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"function"</span><span class="w">
  </span><span class="p">)</span><span class="w">

  </span><span class="n">d_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">(</span><span class="n">do.call</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span><span class="o">^</span><span class="m">2</span><span class="w">

  </span><span class="n">mle_vars</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">[</span><span class="n">idx</span><span class="p">,</span><span class="w"> </span><span class="n">idx</span><span class="p">])</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="m">2</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w">
  </span><span class="p">})</span><span class="w">

  </span><span class="n">d_sq_bars</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w">

  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">k_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">l</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">l_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">l</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">l</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
      </span><span class="n">d_sq_bars</span><span class="p">[</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">l</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w">
        </span><span class="p">(</span><span class="n">n</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w">
        </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">[</span><span class="n">k_idx</span><span class="p">,</span><span class="w"> </span><span class="n">l_idx</span><span class="p">])</span><span class="w"> </span><span class="o">-</span><span class="w">
        </span><span class="n">mle_vars</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w">
        </span><span class="n">mle_vars</span><span class="p">[</span><span class="n">l</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">

  </span><span class="n">n</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq_bars</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">sim_norms</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">means</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">vars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">lapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">rnorm</span><span class="p">(</span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">means</span><span class="p">[</span><span class="n">k</span><span class="p">],</span><span class="w"> </span><span class="n">vars</span><span class="p">[</span><span class="n">k</span><span class="p">])</span><span class="w">
  </span><span class="p">})</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">two_chain_b</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrep</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_norms</span><span class="p">(</span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">nsamp</span><span class="p">)</span><span class="w">
  </span><span class="nf">c</span><span class="p">(</span><span class="n">euclidean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_classic</span><span class="p">(</span><span class="n">x</span><span class="p">),</span><span class="w"> </span><span class="n">frechet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_frechet</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">))</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="n">two_chain_ratios</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">two_chain_diffs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">

</span><span class="n">four_chain_b</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrep</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_norms</span><span class="p">(</span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="n">nsamp</span><span class="p">)</span><span class="w">
  </span><span class="nf">c</span><span class="p">(</span><span class="n">euclidean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_classic</span><span class="p">(</span><span class="n">x</span><span class="p">),</span><span class="w"> </span><span class="n">frechet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_frechet</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">))</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="n">four_chain_ratios</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">four_chain_diffs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">

</span><span class="n">eight_chain_b</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrep</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_norms</span><span class="p">(</span><span class="m">8</span><span class="p">,</span><span class="w"> </span><span class="n">nsamp</span><span class="p">)</span><span class="w">
  </span><span class="nf">c</span><span class="p">(</span><span class="n">euclidean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_classic</span><span class="p">(</span><span class="n">x</span><span class="p">),</span><span class="w"> </span><span class="n">frechet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_frechet</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">))</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="n">eight_chain_ratios</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">eight_chain_diffs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">

</span><span class="n">summary</span><span class="p">(</span><span class="n">two_chain_ratios</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">four_chain_ratios</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">eight_chain_ratios</span><span class="p">)</span><span class="w">

</span><span class="n">summary</span><span class="p">(</span><span class="n">two_chain_diffs</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">four_chain_diffs</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">eight_chain_diffs</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Yes, this is the nicer version. Not shorter, just nicer. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>This is part $n+1$ of the series “oh, so <em>that’s</em> why they stress that so much in intro stats” in my personal learning journey. Proving once again that a great way to get a handle on what frequentist stats actually means is to do Bayesian statistics and ask, “well, shoot, what do I do with these samples?” <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[Previously, I attempted to derive a Fréchet generalization of the PSRF. Then I stumbled around in the dark and stumbled on a form that checks out. Here I’m going to actually do the math I should have done in the first place.]]></summary></entry><entry><title type="html">The Fréchet PSRF, part 4</title><link href="https://afmagee.github.io/frechet-psrf-4/" rel="alternate" type="text/html" title="The Fréchet PSRF, part 4" /><published>2026-07-14T00:00:00+00:00</published><updated>2026-07-14T00:00:00+00:00</updated><id>https://afmagee.github.io/frechet-psrf-4</id><content type="html" xml:base="https://afmagee.github.io/frechet-psrf-4/"><![CDATA[<p>In this final part of what I initially thought would be a single post, I’m going to reiterate the Fréchet generalization of the PSRF worked on in the last parts and then compare it to Whidden and Matsen (2015)’s Gelman-Rubin-like diagnostic.
The math of the comparison is going to change a bit, but the big picture won’t.</p>

<h2 id="recap">Recap</h2>

<p>The Fréchet PSRF is</p>

\[\hat{R}_\mathrm{F} = \sqrt{\frac{\hat{\sigma}_\mathrm{F}^2}{s_\mathrm{F}^2}}\]

<p>The pooled Fréchet sample variance is</p>

\[s_\mathrm{F}^2 = \frac{1}{m} \sum_k s_{\mathrm{F}, k}^2\]

<p>and the per-chain sample variance is</p>

\[s_{\mathrm{F}, k}^2 = \frac{1}{n (n - 1)} \sum_{j &gt; i} d(x_{ki}, x_{kj})^2\]

<p>The biased-from-above $\hat{\sigma}_\mathrm{F}^2$ is</p>

\[\hat{\sigma^2} = \left( 1 - \frac{1}{n} \right) s_\mathrm{F}^2 + \frac{1}{n}B_\mathrm{F}\]

<p>and</p>

\[B_{\mathrm{F}} = \frac{n}{m (m - 1)} \sum_{\ell &gt; k} d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<p>where</p>

\[d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 = 
\frac{1}{n^2} \sum_i \sum_j d(x_{k i}, x_{\ell j})^2
- \frac{1}{n^2} \sum_{j &gt; i} d(x_{k i}, x_{k j})^2
- \frac{1}{n^2} \sum_{j &gt; i} d(x_{\ell i}, x_{\ell j})^2\]

<p>This is almost entirely as laid out in <a href="https://afmagee.github.io/frechet-psrf/">Part 1</a> (and references therein) except this last equation, which I got wrong there and spent a lot of time fixing.</p>

<h2 id="compared-to-the-gelman-rubin-like-diagnostic">Compared to the Gelman-Rubin-like diagnostic</h2>

<p>Let’s start again with the easy term,</p>

\[s_{\text{WM}, k}^2 = \frac{1}{n (n - 1)} \sum_{i = 1}^n \sum_{j = 1}^n d(x_{k i}, x_{k j})^2 = \frac{2}{n (n - 1)} \sum_{j &gt; i} d(x_{k i}, x_{k j})^2 = 2 s_{\text{F}, k}^2\]

<p>That is, summing over the entire (squared) distance matrix yields twice the variance.</p>

<p>The harder term is</p>

\[B_{\text{WM}} = \frac{1}{(m - 1)m n^2} \sum_k \sum_\ell \sum_i \sum_j d(x_{ki}, x_{\ell j})^2\]

<p>The sum here is over the (squared) distance matrix of the concatenated chains, so there’s a block structure.
The diagonal blocks are clearly variance-only terms, and the off-diagonal blocks are between chain comparison terms (which end up including variance terms too).
Last time I tackled this by splitting it up, but having wrangled with these terms more, I’m not sure we want to.
But, note that, per <a href="#detour-1">Detour 1</a>, our work from <a href="https://afmagee.github.io/frechet-psrf-3/">Part 3</a> holds for both these blocks.</p>

<p>So, rewriting this we get,</p>

\[B_{\text{WM}} = \frac{1}{(m - 1)m n^2} \sum_k \sum_\ell \left[ n(n - 1) s_{\mathrm{F}, k}^2 + n(n - 1) s_{\mathrm{F}, \ell}^2
+ n^2 \times d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2
 \right]\]

<p>Now let’s tackle these terms separately.
We get the first two via <a href="#detour-2">Detour 2</a></p>

\[B_{\text{WM}} = \frac{1}{(m - 1)m n^2} \left( 2m^2 n(n - 1) s_{\mathrm{F}}^2 + \sum_k \sum_\ell \left[ n^2 \times d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 \right] \right)\]

<p>And by <a href="#detour-3">Detour 3</a></p>

\[B_{\text{WM}} = \frac{1}{(m - 1)m n^2} \left( 2m^2 n(n - 1) s_{\mathrm{F}}^2 + 2 n m (m - 1) B_{\mathrm{F}} \right)\]

<p>Cleaning up, we get,</p>

\[B_{\text{WM}} = \frac{2m}{(m - 1) n (n - 1)} s_{\mathrm{F}}^2 + \frac{2}{n} B_{\mathrm{F}}\]

<p>The factor of 2 in $s_{\text{WM}}^2$ and $\hat{\sigma}_{\text{WM}}^2$ cancel, but I don’t think we need to finish the math because the overall story is the same as we concluded in Part 1.
As the sample size $n$ goes to infinity, $B_{\text{WM}}$ goes to 0, so the between-chain component here is asymptotically negligible.</p>

<h2 id="detours">Detours</h2>

<p>Treat this like an appendix and ignore it if you like.</p>

<h3 id="detour-1">Detour 1</h3>

<p>Synthesizing some equations from Part 3, we have, for chains $k$ and $\ell$,</p>

\[\frac{1}{n^2} \sum_i \sum_j d(x_{k i}, x_{\ell j})^2 =
\frac{n - 1}{n} s_{\mathrm{F}, k}^2 + \frac{n - 1}{n} s_{\mathrm{F}, \ell}^2
+ d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<p>Multiplying through by $n^2$, we get</p>

\[\sum_i \sum_j d(x_{k i}, x_{\ell j})^2 =
n(n - 1) s_{\mathrm{F}, k}^2 + n(n - 1) s_{\mathrm{F}, \ell}^2
+ n^2 \times d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<p>In the case where $k = \ell$, the distance between means is 0, so,</p>

\[\sum_i \sum_j d(x_{k i}, x_{k j})^2 =
n(n - 1) s_{\mathrm{F}, k}^2 + n(n - 1) s_{\mathrm{F}, k}^2 =
2 n (n - 1) s_{\mathrm{F}, k}^2\]

<p>We can also get to this identity by starting from our variance identity,</p>

\[s_{\mathrm{F}, k}^2 = \frac{1}{n (n - 1)} \sum_{j &gt; i} d(x_{ki}, x_{kj})^2\]

\[2 n (n - 1) s_{\mathrm{F}, k}^2 = \sum_i \sum_j d(x_{ki}, x_{kj})^2\]

<h3 id="detour-2">Detour 2</h3>

\[\sum_k \sum_\ell n(n - 1) s_{\mathrm{F}, k}^2 = \sum_k m n(n - 1) s_{\mathrm{F}, k}^2\]

\[\sum_k \sum_\ell n(n - 1) s_{\mathrm{F}, k}^2 = m^2 n(n - 1) s_{\mathrm{F}}^2\]

<p>In parallel,</p>

\[\sum_k \sum_\ell n(n - 1) s_{\mathrm{F}, \ell}^2 = m^2 n(n - 1) s_{\mathrm{F}}^2\]

<p>So in total,</p>

\[\sum_k \sum_\ell \left[ n(n - 1) s_{\mathrm{F}, k}^2 + n(n - 1) s_{\mathrm{F}, \ell}^2 \right] = 2m^2 n(n - 1) s_{\mathrm{F}}^2\]

<h3 id="detour-3">Detour 3</h3>

<p>We have that,</p>

\[B_{\mathrm{F}} = \frac{n}{m (m - 1)} \sum_{\ell &gt; k} d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<p>But we want to work with all $k$ and $\ell$.
We apply symmetry and the fact that for $k = \ell$ the distance between averages is 0, to note that this simply doubles things.</p>

\[2 B_{\mathrm{F}} = \frac{n}{m (m - 1)} \sum_k \sum_\ell d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<p>so</p>

\[\sum_k \sum_\ell d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 = \frac{2 m (m - 1) B_{\mathrm{F}}}{n}\]

<p>and</p>

\[\sum_k \sum_\ell \left[ n^2 \times d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 \right] = 2 n m (m - 1) B_{\mathrm{F}}\]

<h2 id="code-check">Code check</h2>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">set.seed</span><span class="p">(</span><span class="m">42</span><span class="p">)</span><span class="w">

</span><span class="n">psrf</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w">

  </span><span class="n">stopifnot</span><span class="p">(</span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"list"</span><span class="p">,</span><span class="w"> </span><span class="nf">all</span><span class="p">(</span><span class="n">lengths</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w">

  </span><span class="n">chain_means</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">mean</span><span class="p">)</span><span class="w">
  </span><span class="n">grand_mean</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">chain_means</span><span class="p">)</span><span class="w">

  </span><span class="n">b_n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sum</span><span class="p">((</span><span class="n">chain_means</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">grand_mean</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w">

  </span><span class="n">s_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">sapply</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">var</span><span class="p">))</span><span class="w">
  </span><span class="n">hat_sigma_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">s_sq</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b_n</span><span class="w">

  </span><span class="n">r_hat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">((</span><span class="n">hat_sigma_sq</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">s_sq</span><span class="p">)</span><span class="w">

  </span><span class="n">r_hat</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">psrf_frechet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w">

  </span><span class="n">stopifnot</span><span class="p">(</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"list"</span><span class="p">,</span><span class="w">
    </span><span class="nf">all</span><span class="p">(</span><span class="n">lengths</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">),</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"function"</span><span class="w">
  </span><span class="p">)</span><span class="w">

  </span><span class="n">d_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">(</span><span class="n">do.call</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span><span class="o">^</span><span class="m">2</span><span class="w">

  </span><span class="n">vars</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">[</span><span class="n">idx</span><span class="p">,</span><span class="w"> </span><span class="n">idx</span><span class="p">])</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="m">2</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">
  </span><span class="p">})</span><span class="w">

  </span><span class="n">d_sq_bars</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w">

  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">k_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">l</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">l_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">l</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">l</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
      </span><span class="n">d_sq_bars</span><span class="p">[</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">l</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w">
        </span><span class="p">(</span><span class="n">n</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w">
        </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">[</span><span class="n">k_idx</span><span class="p">,</span><span class="w"> </span><span class="n">l_idx</span><span class="p">])</span><span class="w"> </span><span class="o">-</span><span class="w">
        </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">vars</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w">
        </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">vars</span><span class="p">[</span><span class="n">l</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">

  </span><span class="n">b_n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq_bars</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">

  </span><span class="n">s_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">vars</span><span class="p">)</span><span class="w">
  </span><span class="n">hat_sigma_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">s_sq</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b_n</span><span class="w">

  </span><span class="n">r_hat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">(</span><span class="n">hat_sigma_sq</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">s_sq</span><span class="p">)</span><span class="w">

  </span><span class="n">r_hat</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">wm_stat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w">

  </span><span class="n">stopifnot</span><span class="p">(</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"list"</span><span class="p">,</span><span class="w">
    </span><span class="nf">all</span><span class="p">(</span><span class="n">lengths</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">),</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"function"</span><span class="w">
  </span><span class="p">)</span><span class="w">

  </span><span class="n">d_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">(</span><span class="n">do.call</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span><span class="o">^</span><span class="m">2</span><span class="w">

  </span><span class="n">vars</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">[</span><span class="n">idx</span><span class="p">,</span><span class="w"> </span><span class="n">idx</span><span class="p">])</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">
  </span><span class="p">})</span><span class="w">

  </span><span class="n">b_n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">((</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">m</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="o">^</span><span class="m">3</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">)</span><span class="w">

  </span><span class="n">s_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">vars</span><span class="p">)</span><span class="w">
  </span><span class="n">hat_sigma_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">(</span><span class="m">1</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">s_sq</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b_n</span><span class="w">

  </span><span class="n">r_hat</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">sqrt</span><span class="p">((</span><span class="n">hat_sigma_sq</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">s_sq</span><span class="p">)</span><span class="w">

  </span><span class="n">r_hat</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">sim_norms</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">means</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">vars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">lapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">rnorm</span><span class="p">(</span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">means</span><span class="p">[</span><span class="n">k</span><span class="p">],</span><span class="w"> </span><span class="n">vars</span><span class="p">[</span><span class="n">k</span><span class="p">])</span><span class="w">
  </span><span class="p">})</span><span class="w">
</span><span class="p">}</span><span class="w">


</span><span class="n">nrep</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">
</span><span class="n">nsamp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">

</span><span class="n">four_chain_psrf</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">t</span><span class="p">(</span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrep</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_norms</span><span class="p">(</span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="n">nsamp</span><span class="p">)</span><span class="w">
  </span><span class="nf">c</span><span class="p">(</span><span class="w">
    </span><span class="n">euclidean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">psrf</span><span class="p">(</span><span class="n">x</span><span class="p">),</span><span class="w">
    </span><span class="n">frechet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">psrf_frechet</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">),</span><span class="w">
    </span><span class="n">wm_2015</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">wm_stat</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w">
</span><span class="p">}))</span><span class="w">

</span><span class="n">r</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">range</span><span class="p">(</span><span class="n">four_chain_psrf</span><span class="p">)</span><span class="w">

</span><span class="n">plot</span><span class="p">(</span><span class="w">
  </span><span class="n">four_chain_psrf</span><span class="p">[,</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="m">2</span><span class="p">],</span><span class="w">
  </span><span class="n">xlim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w">
  </span><span class="n">ylim</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">r</span><span class="p">,</span><span class="w">
  </span><span class="n">xlab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Standard PSRF"</span><span class="p">,</span><span class="w">
  </span><span class="n">ylab</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Frechet comparisons"</span><span class="w">
</span><span class="p">)</span><span class="w">
</span><span class="n">abline</span><span class="p">(</span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"red"</span><span class="p">)</span><span class="w">
</span><span class="n">legend</span><span class="p">(</span><span class="w">
  </span><span class="s2">"topleft"</span><span class="p">,</span><span class="w">
  </span><span class="n">legend</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"Fréchet"</span><span class="p">,</span><span class="w"> </span><span class="s2">"WM2015"</span><span class="p">),</span><span class="w">
  </span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="s2">"black"</span><span class="p">,</span><span class="w"> </span><span class="s2">"blue"</span><span class="p">),</span><span class="w">
  </span><span class="n">border</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">NA</span><span class="p">,</span><span class="w">
  </span><span class="n">bty</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"n"</span><span class="w">
</span><span class="p">)</span><span class="w">
</span><span class="n">points</span><span class="p">(</span><span class="n">four_chain_psrf</span><span class="p">[,</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="m">3</span><span class="p">)],</span><span class="w"> </span><span class="n">col</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"blue"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[In this final part of what I initially thought would be a single post, I’m going to reiterate the Fréchet generalization of the PSRF worked on in the last parts and then compare it to Whidden and Matsen (2015)’s Gelman-Rubin-like diagnostic. The math of the comparison is going to change a bit, but the big picture won’t.]]></summary></entry><entry><title type="html">The Fréchet PSRF, part 2</title><link href="https://afmagee.github.io/frechet-psrf-2/" rel="alternate" type="text/html" title="The Fréchet PSRF, part 2" /><published>2026-05-19T00:00:00+00:00</published><updated>2026-05-19T00:00:00+00:00</updated><id>https://afmagee.github.io/frechet-psrf-2</id><content type="html" xml:base="https://afmagee.github.io/frechet-psrf-2/"><![CDATA[<p><a href="https://afmagee.github.io/frechet-psrf/">Previously</a>, I attempted to derive a Fréchet generalization of the PSRF.
And wouldn’t you know it, I was wrong.
Here, I attempt to correct my math.</p>

<h2 id="disclaimer">Disclaimer</h2>

<p>This post shows both the benefits and dangers of intuition.
I managed to guess something close enough to the nature of the error I made <a href="https://afmagee.github.io/frechet-psrf/">last time</a> to fix it.
But wrong enough to cascade and require other weird re-correcting.
I’m leaving it up because math is messy and sometimes we’re all stumbling around in the dark.
But I’d suggest skipping to <a href="https://afmagee.github.io/frechet-psrf-3/">part 3</a>.</p>

<h2 id="what-did-i-test-and-how-wrong-was-i">What did I test and how wrong was I?</h2>

<p>It’s always best to start relatively simple, so I started out by testing $B$.
Yes, $s^2$ would have been simpler, but that’s already been through peer review, so we’ll leave it alone.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>
But this is the heart of what we need to be right, and it reduces oh so slightly the number of places an error could creep in compared to jumping straight to $\hat{R}$ or $\hat{\sigma}^2$.</p>

<p>How was I testing $B$? With the code shown <a href="#code-appendix">below</a>, just implementing the math in my previous post.
So, each “chain” is a bunch of IID normals, and chain $k$ has mean $k$.
The comparison, as mentioned last time, is to the regular PSRF for real-valued random variables.
If the generalization is right, in this case the math should collapse and the answers should be identical.</p>

<p>The good news: my $B_{\mathrm{F}}$ was <em>close</em> to the standard $B$.</p>

<p>The bad news: my $B_{\mathrm{F}}$ was <em>stochastically</em> wrong, and wrongness varied in both $m$ and $n$.</p>

<h2 id="fixing-it-plan-of-attack">Fixing it: plan of attack</h2>

<p>Now, assuming that I implemented the math right in code, that is, that this is actually a math error, where to look?
The likely culprit would seem to be that I’ve got a biased estimator of $\mathbb{E}[d(X_{k \cdot}, X_{\ell \cdot})^2]$.
This doesn’t exactly explain the $m$-dependency, which could be a <em>separate</em> error.
But if it’s, say, off by a Bessel correction that would explain why it seems to converge as $n \to \infty$.
It’s also the only bit of math in $B$ defining a random variable that hasn’t previously been vetted.</p>

<h2 id="fixing-it-no-plan-survives-contact-with-the-enemy">Fixing it: no plan survives contact with the enemy</h2>

<p>Previously, I said that if we have $n$ samples of a real-valued variable $\xi$ and $n$ independent samples of a real-valued variable $\upsilon$, then</p>

\[\hat{\mathbb{E}}[d(\xi, \upsilon)^2] = \frac{1}{n^2} \sum_i \sum_j d(\xi_i, \upsilon_j)^2\]

<p>Going back to Euclidean land, that’s saying</p>

\[\hat{\mathbb{E}}[(\xi - \upsilon)^2] = \frac{1}{n^2} \sum_i \sum_j (\xi_i - \upsilon_j)^2\]

<p>At this point, I will admit, I took a leap of faith, and assumed that the right answer looked Bessel-corrected.
That is, I assumed the right estimator is</p>

\[\hat{\mathbb{E}}[(\xi - \upsilon)^2] = \frac{1}{n (n - 1)} \sum_i \sum_j (\xi_i - \upsilon_j)^2\]

<p>The good news is that this did banish the stochastic part of my being wrong, but it didn’t make it go away.
The bad news is now I need to try to figure out why this is The Right Thing To Do.</p>

<p>Going back to the math, which is the same equation we are borne back ceaselessly unto, we have, without hats</p>

\[\mathbb{E}[(\xi - \upsilon)^2] = \mathrm{Var}(\xi) + \mathrm{Var}(\upsilon) - 2\mathrm{Cov}(\xi,\upsilon) + \left( \mathbb{E}[\xi] - \mathbb{E}[\upsilon] \right)^2\]

<p>Staring at this isn’t proving all that illuminating, so I probably should stare at the Bessel correction’s proof instead.
But I’m going to save that for another time and see if I can’t flounder my way to figuring out the right answer first, and then prove it later.</p>

<p>What I can say now is that, with this correction, but no other math changed, $B_{\mathrm{F}} / B$:</p>
<ul>
  <li>Looked like it stopped being stochastic</li>
  <li>Looked like it became constant in $m$</li>
  <li>Produced some suspiciously informative numbers as a function of $n$</li>
</ul>

<p>In particular, it looks like with the corrected $\hat{\mathbb{E}}[(\xi - \upsilon)^2]$, $B_{\mathrm{F}} / B = n / (n - 1)$.</p>

<h2 id="the-right-b_mathrmf-i-hope">The right $B_{\mathrm{F}}$, I hope</h2>

<p>My numerical experiments suggest that, to within decent numerical tolerances, The Right Generalization of $B$ is</p>

\[B_{\mathrm{F}} = \frac{n - 1}{m (m - 1)} \sum_{\ell &gt; k} d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<p>where</p>

\[d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 = \left( \mathbb{E}[X_{k \cdot}] - \mathbb{E}[X_{\ell \cdot}] \right)^2 = \\
\frac{1}{n (n - 1)} \sum_{i,j} d(X_{k i}, X_{\ell j})^2\\
- \frac{1}{n (n - 1)} \sum_{j &gt; i} d(x_{ki}, x_{kj})^2\\
- \frac{1}{n (n - 1)} \sum_{j &gt; i} d(x_{\ell i}, x_{\ell j})^2\]

<h2 id="next-steps">Next steps</h2>

<p>Armed with something that at least checks out computationally, we need to understand why it’s right mathematically.
I can allow myself to hope that the average squared distance bit is just a Bessel correction.
But then why did the leading factor of $n$ become a factor of $n - 1$?
Well, time (or someone smarter) will tell eventually.</p>

<h2 id="code-appendix">Code appendix</h2>

<div class="language-R highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">set.seed</span><span class="p">(</span><span class="m">42</span><span class="p">)</span><span class="w">

</span><span class="n">nrep</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">
</span><span class="n">nsamp</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">100</span><span class="w">

</span><span class="n">b_classic</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w">

  </span><span class="n">stopifnot</span><span class="p">(</span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"list"</span><span class="p">,</span><span class="w"> </span><span class="nf">all</span><span class="p">(</span><span class="n">lengths</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">))</span><span class="w">

  </span><span class="n">chain_means</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">mean</span><span class="p">)</span><span class="w">
  </span><span class="n">grand_mean</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">mean</span><span class="p">(</span><span class="n">chain_means</span><span class="p">)</span><span class="w">

  </span><span class="n">n</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">sum</span><span class="p">((</span><span class="n">chain_means</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">grand_mean</span><span class="p">)</span><span class="o">^</span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">b_frechet</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">m</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w">
  </span><span class="n">n</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nf">length</span><span class="p">(</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]])</span><span class="w">

  </span><span class="n">stopifnot</span><span class="p">(</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"list"</span><span class="p">,</span><span class="w">
    </span><span class="nf">all</span><span class="p">(</span><span class="n">lengths</span><span class="p">(</span><span class="n">x</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">n</span><span class="p">),</span><span class="w">
    </span><span class="nf">class</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s2">"function"</span><span class="w">
  </span><span class="p">)</span><span class="w">

  </span><span class="n">d_sq</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">as.matrix</span><span class="p">(</span><span class="n">dist_fun</span><span class="p">(</span><span class="n">do.call</span><span class="p">(</span><span class="n">c</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)))</span><span class="o">^</span><span class="m">2</span><span class="w">

  </span><span class="n">vars</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">[</span><span class="n">idx</span><span class="p">,</span><span class="w"> </span><span class="n">idx</span><span class="p">])</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="m">2</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w">
  </span><span class="p">})</span><span class="w">

  </span><span class="n">d_sq_bars</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">matrix</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="w"> </span><span class="n">nrow</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">m</span><span class="p">)</span><span class="w">

  </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">k_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">k</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">l</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="p">(</span><span class="n">k</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="n">m</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="n">l_idx</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="p">((</span><span class="n">l</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="o">:</span><span class="p">(</span><span class="n">l</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">n</span><span class="p">)</span><span class="w">
      </span><span class="n">d_sq_bars</span><span class="p">[</span><span class="n">k</span><span class="p">,</span><span class="w"> </span><span class="n">l</span><span class="p">]</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="m">1</span><span class="w"> </span><span class="o">/</span><span class="w">
        </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w">
        </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq</span><span class="p">[</span><span class="n">k_idx</span><span class="p">,</span><span class="w"> </span><span class="n">l_idx</span><span class="p">])</span><span class="w"> </span><span class="o">-</span><span class="w">
        </span><span class="n">vars</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w">
        </span><span class="n">vars</span><span class="p">[</span><span class="n">l</span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">

  </span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">m</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="m">1</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="nf">sum</span><span class="p">(</span><span class="n">d_sq_bars</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">sim_norms</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">means</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="n">vars</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">rep</span><span class="p">(</span><span class="m">1</span><span class="p">,</span><span class="w"> </span><span class="n">m</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">lapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">m</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="n">rnorm</span><span class="p">(</span><span class="n">n</span><span class="p">,</span><span class="w"> </span><span class="n">means</span><span class="p">[</span><span class="n">k</span><span class="p">],</span><span class="w"> </span><span class="n">vars</span><span class="p">[</span><span class="n">k</span><span class="p">])</span><span class="w">
  </span><span class="p">})</span><span class="w">
</span><span class="p">}</span><span class="w">

</span><span class="n">two_chain_b</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrep</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_norms</span><span class="p">(</span><span class="m">2</span><span class="p">,</span><span class="w"> </span><span class="n">nsamp</span><span class="p">)</span><span class="w">
  </span><span class="nf">c</span><span class="p">(</span><span class="n">euclidean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_classic</span><span class="p">(</span><span class="n">x</span><span class="p">),</span><span class="w"> </span><span class="n">frechet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_frechet</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">))</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="n">two_chain_ratios</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">two_chain_diffs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">two_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">

</span><span class="n">four_chain_b</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrep</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_norms</span><span class="p">(</span><span class="m">4</span><span class="p">,</span><span class="w"> </span><span class="n">nsamp</span><span class="p">)</span><span class="w">
  </span><span class="nf">c</span><span class="p">(</span><span class="n">euclidean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_classic</span><span class="p">(</span><span class="n">x</span><span class="p">),</span><span class="w"> </span><span class="n">frechet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_frechet</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">))</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="n">four_chain_ratios</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">four_chain_diffs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">four_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">

</span><span class="n">eight_chain_b</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sapply</span><span class="p">(</span><span class="m">1</span><span class="o">:</span><span class="n">nrep</span><span class="p">,</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">idx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">x</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">sim_norms</span><span class="p">(</span><span class="m">8</span><span class="p">,</span><span class="w"> </span><span class="n">nsamp</span><span class="p">)</span><span class="w">
  </span><span class="nf">c</span><span class="p">(</span><span class="n">euclidean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_classic</span><span class="p">(</span><span class="n">x</span><span class="p">),</span><span class="w"> </span><span class="n">frechet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">b_frechet</span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">dist</span><span class="p">))</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="n">eight_chain_ratios</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">
</span><span class="n">eight_chain_diffs</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"frechet"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">eight_chain_b</span><span class="p">[</span><span class="s2">"euclidean"</span><span class="p">,</span><span class="w"> </span><span class="p">]</span><span class="w">

</span><span class="n">summary</span><span class="p">(</span><span class="n">two_chain_ratios</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">four_chain_ratios</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">eight_chain_ratios</span><span class="p">)</span><span class="w">

</span><span class="n">summary</span><span class="p">(</span><span class="n">two_chain_diffs</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">four_chain_diffs</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">eight_chain_diffs</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Not that peer review guarantees correctness. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[Previously, I attempted to derive a Fréchet generalization of the PSRF. And wouldn’t you know it, I was wrong. Here, I attempt to correct my math.]]></summary></entry><entry><title type="html">The Fréchet PSRF</title><link href="https://afmagee.github.io/frechet-psrf/" rel="alternate" type="text/html" title="The Fréchet PSRF" /><published>2026-05-17T00:00:00+00:00</published><updated>2026-05-17T00:00:00+00:00</updated><id>https://afmagee.github.io/frechet-psrf</id><content type="html" xml:base="https://afmagee.github.io/frechet-psrf/"><![CDATA[<p><a href="https://projecteuclid.org/journals/statistical-science/volume-7/issue-4/Inference-from-Iterative-Simulation-Using-Multiple-Sequences/10.1214/ss/1177011136.full">Gelman and Rubin (1992)</a> proposed the Potential Scale Reduction Factor (PSRF), perhaps most commonly know known as “Rhat”<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.
This is, give or take the field dropping some terms from the original for convenience, perhaps the most widely used way of comparing two or more MCMC chains for diagnosing MCMC convergence.
<a href="https://academic.oup.com/sysbio/article/64/3/472/1632660">Whidden and Matsen (2015)</a> proposed what they called a “Gelman-Rubin-like” diagnostic as a generalization to phylogenies.</p>

<p>I was, when I started writing this, pretty sure that at some point five or six years ago I wrote out some math that shows this is a valid Fréchet generalization of the PSRF in much the same way one can generalize the <a href="https://projecteuclid.org/journals/bayesian-analysis/volume-19/issue-2/How-Trustworthy-Is-Your-Tree-Bayesian-Phylogenetic-Effective-Sample-Size/10.1214/22-BA1339.full">effective sample size</a>.
But the more I look at all this math, the more I think that I didn’t actually manage to do so.
Or, perhaps, I got extraordinarily lucky then and goofed this time around.</p>

<h2 id="disclaimer">Disclaimer</h2>

<p>I didn’t quite get the math right here.
The big picture is fine, but some of the details are wrong.
I’m leaving it up because it’s mostly right, because I don’t want to re-write all the sidebars here, and because it’s honest to show that mistakes happen when doing math (and science).</p>

<p>Check out <a href="https://afmagee.github.io/frechet-psrf-3/">Part 3</a> for the fix, or <a href="https://afmagee.github.io/frechet-psrf-2/">Part 4</a> for the final correct version and comparison to Whidden and Matsen.</p>

<h2 id="psrf">PSRF</h2>

<p>Assume you have $m$ MCMC runs each with $n$ samples.
The form of the PSRF that you usually see written out<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">2</a></sup> is a ratio of standard deviations</p>

\[\hat{R} = \sqrt{\frac{\hat{\sigma^2}}{s^2}}\]

<p>where $s^2$ is the average within-chain sample variance and $\hat{\sigma^2}$ is</p>

\[\hat{\sigma^2} = \left( 1 - \frac{1}{n} \right) s^2 + \frac{1}{n}B\]

<p>which is constructed to be biased upwards.
Note that while this looks like it is constructed such that $\hat{\sigma^2} \to s^2$ as $n \to \infty$, the definition of $B$ has a cancelling $n$ in it</p>

\[B = \frac{n}{m - 1}\sum_{k} (\bar{X}_{k \cdot} - \bar{X})^2\]

<p>So, when all independent MCMC runs are sampling the same density, $B \to 0$ as $n \to \infty$,<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">3</a></sup> so $\hat{R} \to 1$.</p>

<h3 id="to-b-or-not-to-b">To $B$ or not to $B$</h3>

<p>It may prove helpful later<sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">4</a></sup> to have some idea what $B$ really is, if we’re going to start trying to generalize it and otherwise muck with math in its general vicinity.</p>

<p>To start, let us adopt a mixture distribution view of the situation.
We have $m$ component distributions with equal weight $1/m$ and a categorical variable for which component $k$ a given one of the $mn$ total samples comes from.
Let $Z$ be the component-defining categorical variable.</p>

<p>Now we have to be careful of randomness.
The sample mean of chain $k$, $\bar{X}_{k \cdot}$, is a random variable, as is the grand mean $\bar{X}$.
They are also <em>estimates</em></p>

\[\bar{X}_{k \cdot} = \hat{\mathbb{E}}[X \mid Z = k]\]

<p>and</p>

\[\bar{X} = \hat{\mathbb{E}}[X] = \hat{\mathbb{E}}[\mathbb{E}[X \mid Z]]\]

<p>In this light, $B/n$, give or take a Bessel correction, can be written</p>

\[\frac{B}{n}
= \frac{1}{m - 1} \sum_{k} (\hat{\mathbb{E}}[X \mid Z] - \hat{\mathbb{E}}[\mathbb{E}[X \mid Z]])^2\]

<p>Now, for the sake of sanity, let’s drop the hats.
Clearly, $B/n$ is a sample-based estimate of something, and if we look at the RHS here, with or without hats, we can understand what.</p>

\[\frac{1}{m - 1} \sum_{k} (\mathbb{E}[X \mid Z] - \mathbb{E}[\mathbb{E}[X \mid Z]])^2
= \mathbb{E}[(\mathbb{E}[X \mid Z] - \mathbb{E}[\mathbb{E}[X \mid Z]])^2]
= \mathrm{Var}(\mathbb{E}[X \mid Z])\]

<p>So, $B/n$ is a term <em>estimating</em> the variance in the mixture component’s means.
It feels worth noting, since we’ve gone to the trouble to write this all out, that this is one of two components in the <a href="https://en.wikipedia.org/wiki/Law_of_total_variance">law of total variance</a></p>

\[\mathrm{Var}(X) = \mathbb{E}[\mathrm{Var}(X \mid Z]) + \mathrm{Var}(\mathbb{E}[X \mid Z])\]

<p>For mixture distributions, this tells us that the total (marginal) variance is bigger than the average variance by a factor of how far apart the means of the components are.</p>

<h2 id="whidden-and-matsen-2015">Whidden and Matsen (2015)</h2>

<p>Index chains such that $x_{ki}$ is the $i$th sample from chain $k$.
Let $
d(x,y)$ be a distance function, such as the SPR distance, as Whidden and Matsen (2015) used.
They proposed to quantify convergence with</p>

\[\hat{R}_{\text{WM}} = \sqrt{\frac{\hat{\sigma^2}_{\text{WM}}}{s_{\text{WM}}^2}}\]

<p>where $s_{\text{WM}}^2$ is the average of the $m$ per-chain quantities</p>

\[s_{\text{WM}, k}^2 = \frac{1}{n (n - 1)} \sum_{i} \sum_{j} d(x_{ki}, x_{kj})^2\]

<p>and where $\hat{\sigma^2}_{\text{WM}}$ is calculated analogously to $\hat{\sigma^2}$ but using</p>

\[B_{\text{WM}} = \frac{1}{(m - 1)m n^2} \sum_k \sum_\ell \sum_i \sum_j (x_{ki}, x_{\ell j})^2\]

<h2 id="my-memory-is-made-the-fool">My memory is made the fool</h2>

<p>When I started writing this, I thought I was going to unpack Whidden and Matsen’s equation rather directly.
After all, I did it before, right?
Instead, it turned out to be easier to derive the ingredients of a Fréchet-generalized PSRF from scratch, which we’ll do in this section, and in the next section we’ll compare them.</p>

<p>I like to think of Fréchet generalizations as what you get when you substitute (squared) arbitrary distance functions for (squared) Euclidean distances.
For a distribution on real numbers, the mean is the value minimizing the squared Euclidian distance to all other values, weighting by the distibution’s PDF or PMF.
The sample mean is the point minimizing the squared Euclidean distance to the sampled values.
The variance is the average squared Euclidean distance from the mean to the rest of the distribution, weighted by the PDF or PMF.
Give or take a Bessel correction, the sample variance is the average squared Euclidean distance from the sample mean to the sampled values.
Remove the qualifier “Euclidean” and you’re in Fréchet land.</p>

<p>Now let’s look at the two key terms in the PSRF and see what that looks like in Fréchet land.</p>

<h3 id="the-easy-one-s2">The easy one: $s^2$</h3>

<p>The Fréchet sample variance of a single MCMC run is, per Supplementary Equation 15 of <a href="https://projecteuclid.org/journals/bayesian-analysis/volume-19/issue-2/How-Trustworthy-Is-Your-Tree-Bayesian-Phylogenetic-Effective-Sample-Size/10.1214/22-BA1339.full">a paper of mine on the ESS of phylogenetic trees</a></p>

\[s_{\text{F}, k}^2 = \frac{1}{n (n - 1)} \sum_{j &gt; i} d(x_{ki}, x_{kj})^2\]

<h3 id="the-harder-one-b">The harder one: $B$</h3>

<p>The biased-from-above $\sigma^2$ term, or equivalently $B$, is a bit more complex.
Several initial attempts here led me to failure, which I won’t bore the universe with.<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>
To get there, we take a somewhat more circuitous path, including some helpfully unnumbered equations<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup> from the supplement to my <a href="https://projecteuclid.org/journals/bayesian-analysis/volume-19/issue-2/How-Trustworthy-Is-Your-Tree-Bayesian-Phylogenetic-Effective-Sample-Size/10.1214/22-BA1339.full">aforementioned tree ESS paper</a> (see the section entitled “The frechetCorrelationESS”).</p>

<p>One of these equations states that, for (real-valued) random variables $\xi$ and $\upsilon$,</p>

\[\mathbb{E}[(\xi - \upsilon)^2] = \mathrm{Var}(\xi) + \mathrm{Var}(\upsilon) - 2\mathrm{Cov}(\xi,\upsilon) + \left( \mathbb{E}[\xi] - \mathbb{E}[\upsilon] \right)^2\]

<p>In the tree ESS paper, we used this equation to estimate the covariance, because we could separately estimate the variances, and because we could chant “MCMCCLT” and call the last term 0.
But for our purposes here, we have samples from independent MCMC runs, so $\xi$ and $\upsilon$ are $X_{k \cdot}$ and $X_{\ell \cdot}$, and thus their covariance is 0.<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup> 
We can still get the variances separately, so if we rearrange the equation, make the Fréchet substitution of $d(\xi,\upsilon)^2$ for $(\xi - \upsilon)^2$, and use our chain-based variables, we get</p>

\[\left( \mathbb{E}[X_{k \cdot}] - \mathbb{E}[X_{\ell \cdot}] \right)^2 = \mathbb{E}[(X_{k \cdot} - X_{\ell \cdot})^2] - \mathrm{Var}(X_{k \cdot}) - \mathrm{Var}(X_{\ell \cdot})\]

<p>The practical use of this equation is <em>estimating</em> the LHS by using the estimates we get for the terms on the RHS.
What estimates, you ask?
The variances are <a href="#the-easy-one">as above</a>.
The remaining term should be estimable as the average of all $X_{k i}$, $X_{\ell j}$ comparisons.<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">8</a></sup>
Putting that together, giving it a convenient shorthand for later, and not putting hats over terms that deserve it because I can’t make <code class="language-plaintext highlighter-rouge">\widehat</code> work in markdown LaTeX, we get</p>

\[d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 = \left( \mathbb{E}[X_{k \cdot}] - \mathbb{E}[X_{\ell \cdot}] \right)^2 = \\
\frac{1}{n^2} \sum_{i,j} d(X_{k i}, X_{\ell j})^2\\
- \frac{1}{n (n - 1)} \sum_{j &gt; i} d(x_{ki}, x_{kj})^2\\
- \frac{1}{n (n - 1)} \sum_{j &gt; i} d(x_{\ell i}, x_{\ell j})^2\]

<p>Now, right about now it looks like we’ve solved the Wrong Problem.
We’ve got a difference between chain means pairwise, but that’s not what $B/n$ is.
But if we look at the equations that gave us the Fréchet variance, we can turn this into what we want.
For $n$ samples of a variable $X$,</p>

\[\frac{1}{n - 1} \sum_i (X_i - \bar{X})^2 = \frac{1}{n(n - 1)} \sum_{j &gt; i} (X_i - X_j)^2\]

<p>Instead, we consider the $m$ sample <em>means</em> of the chains, and we get</p>

\[\frac{1}{m - 1} \sum_i (\bar{X}_k - \bar{X})^2 = \frac{1}{m(m - 1)} \sum_{\ell &gt; k} (\bar{X}_k - \bar{X}_\ell)^2\]

<p>And there we have it, the LHS is $B/n$ and the RHS we just showed how to compute.</p>

\[\frac{B_{\mathrm{F}}}{n} = \frac{1}{m (m - 1)} \sum_{\ell &gt; k} d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2\]

<h2 id="wherefore-art-thou-fréchet">Wherefore art thou Fréchet</h2>

<p>Now we can interpret Whidden and Matsen (2015) in a Fréchet context.</p>

<p>The $1 / (n (n - 1))$ term in both $s_{\text{F}, k}^2$ and $s_{\text{WM}, k}^2$ is identical, but the sum for the Fréchet variance is restricted to the upper (or lower) triangular portion of the matrix.
As this is a distance matrix, the diagonal is $\mathbf{0}$, and the two triangular portions are equal.
Thus</p>

\[s_{\text{WM}, k}^2 = 2 s_{\text{F}, k}^2\]

<p>To understand $B_{\text{WM}}$, we need to do some rewriting of what we wrote above.</p>

<p>We’ll start by trying to get a birds eye view of where we’re going.
We’ll use some dubious notation to revisit and slightly tweak an earlier definition of $d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2$, yielding</p>

\[d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2 = 
\mathbb{E}[d(X_{k \cdot}, X_{\ell \cdot})^2]
- \mathrm{Var}(X_{k \cdot})
- \mathrm{Var}(X_{\ell \cdot})\]

<p>Plugging this into our compact definition of $B_{\mathrm{F}}$, we get</p>

\[B_{\mathrm{F}} = \frac{n}{m (m - 1)} \sum_{\ell &gt; k}
\left[
\mathbb{E}[d(X_{k \cdot}, X_{\ell \cdot})^2]
- \mathrm{Var}(X_{k \cdot})
- \mathrm{Var}(X_{\ell \cdot})
\right]\]

<p>The variance terms each show up $(m - 1)$ times, one each for each $k \neq \ell$ pair.
Condensing those terms and then cleaning up the leading fractions accordingly, we get</p>

\[B_{\mathrm{F}} = 
\frac{n}{m (m - 1)} \left[ \sum_{\ell &gt; k} \mathbb{E}[d(X_{k \cdot}, X_{\ell \cdot})^2] \right]
- \frac{n}{m} \left[ \sum_{k} \mathrm{Var}(X_{k \cdot})]
\right]\]

<p>At this point, we can perhaps finally see where this is going if we think in terms of the matrix of (squared) distances between all $mn$ samples in all chains.
This is a block matrix, including both within-chain ($k = \ell$) terms and between-chain ($k \neq \ell$) terms.
$B_{\mathrm{F}}$ is the sum of a term proportional to the sum of the upper (or lower, if you’d rather) diagonal blocks and a term proportional to the sum of the diagonal blocks, but we’re subtracting the diagonal terms.
To be properly clear, let’s put this in more explicit sum form.
So, now we go backwards, and plug in our actual definitions from when we defined $d(\bar{X}_{k \cdot}, \bar{X}_{\ell \cdot})^2$, yielding</p>

\[B_{\mathrm{F}} = 
\frac{n}{m (m - 1)} \left[ \sum_k \sum_{\ell &gt; k} \sum_i \sum_j \frac{1}{n^2} d(x_{k i}, x_{\ell j})^2 \right]\\
- \frac{n}{m} \left[ \sum_{k} \sum_i \sum_{j &gt; i} \frac{1}{n (n - 1)}  d(x_{ki}, x_{kj})^2]
\right]\]

<p>Cleaning up, we get</p>

\[B_{\mathrm{F}} = 
\frac{1}{n m (m - 1)} \left[ \sum_k \sum_{\ell &gt; k} \sum_i \sum_j d(x_{k i}, x_{\ell j})^2 \right]\\
- \frac{1}{(n - 1) m} \left[ \sum_{k} \sum_i \sum_{j &gt; i} d(x_{ki}, x_{kj})^2]
\right]\]

<p>Now we can make that first term a sum over all off-diagonal blocks, and compensate with a factor of $1/2$ in the leading fraction because distance matrices are symmetric.</p>

\[B_{\mathrm{F}} = 
\frac{1}{2 n m (m - 1)} \left[ \sum_k \sum_{\ell \neq k} \sum_i \sum_j d(x_{k i}, x_{\ell j})^2 \right]\\
- \frac{1}{(n - 1) m} \left[ \sum_{k} \sum_i \sum_{j &gt; i} d(x_{ki}, x_{kj})^2]\right]\]

<p>We’re almost done.
$B_{\mathrm{F}}$ is a sum over the entire matrix, diagonals and all, so let’s add them back into our leading term (and balance the equation as needed).</p>

\[B_{\mathrm{F}} = 
\frac{1}{2 n m (m - 1)} \left[ \sum_k \sum_\ell \sum_i \sum_j d(x_{k i}, x_{\ell j})^2 \right]\\
- \frac{1}{2 n m (m - 1)} \left[ \sum_{k} \sum_i \sum_{j &gt; i} d(x_{ki}, x_{kj})^2] \right]\\
- \frac{1}{(n - 1) m} \left[ \sum_{k} \sum_i \sum_{j &gt; i} d(x_{ki}, x_{kj})^2] \right]\]

<p>And now we clean up.</p>

\[B_{\mathrm{F}} = 
\frac{1}{2 n m (m - 1)} \left[ \sum_k \sum_\ell \sum_i \sum_j d(x_{k i}, x_{\ell j})^2 \right]\\
- \left(\frac{1}{2 n m (m - 1)}  + \frac{1}{(n - 1) m} \right) \left[ \sum_{k} \sum_i \sum_{j &gt; i} d(x_{ki}, x_{kj})^2] \right]\]

<p>At this point, we may have forgotten that</p>

\[B_{\text{WM}} = \frac{1}{(m - 1)m n^2} \sum_k \sum_\ell \sum_i \sum_j d(x_{ki}, x_{\ell j})^2\]

<p>So, we have</p>

<!--
$$
B_{\text{WM}} = \frac{1}{(m - 1)m n^2} \Theta
$$

$$
\Theta = B_{\text{WM}} \times (m - 1)m n^2
$$

$$
B_{\mathrm{F}} = 
\frac{(m - 1)m n^2}{2 n m (m - 1)} B_{\text{WM}}\\
- \left(\frac{1}{2 n m (m - 1)}  + \frac{1}{(n - 1) m} \right) \left[ \sum_{k} \sum_i \sum_{j > i} d(x_{ki}, x_{kj})^2] \right]
$$ -->

\[B_{\mathrm{F}} = 
\frac{n}{2} B_{\text{WM}}
- \left(\frac{1}{2 n m (m - 1)}  + \frac{1}{(n - 1) m} \right) \left[ \sum_{k} \sum_i \sum_{j &gt; i} d(x_{ki}, x_{kj})^2] \right]\]

<p>Alternately</p>

\[B_{\text{WM}} = 
\frac{2}{n} B_{\mathrm{F}} + \left(\frac{1}{n^2 m (m - 1)}  + \frac{2}{n(n - 1) m} \right) \left[ \sum_{k} \sum_i \sum_{j &gt; i} d(x_{ki}, x_{kj})^2] \right]\]

<h2 id="tldr">TL;DR</h2>

<p>This has gotten excessively long for a blog post, so let’s wrap it up.
Where are we?</p>

<p>If the math I did here is right, and that is not a foregone conclusion, then Whidden and Matsen’s proposed Gelman-Rubin-like diagnostic:</p>
<ul>
  <li>Is Gelmen-Rubin-like. We can see that it’s related to a Fréchet generalization of the PSRF.</li>
  <li>Is not a Fréchet generalization of the PSRF. Its $B$ term downweights the between-chain variance and upweights the within-chain variation. Asymptotically in chain length $n$ the within-chain bit goes to zero and $B_{\text{WM}} \to 2/n \times B_{\mathrm{F}}$. So we might expect that it is relatively insensitive to between-chain variation.</li>
</ul>

<p>What still needs to be done?
Obviously I need to check my math.
But that’s a lot of math to check, so a test is in order.
If I got the math right, and I implement that math right in code, then for a real-valued variable, if I use the Euclidean distance, I ought to be able to recover the non-Fréchet PSRF with what I claim is the Fréchet PSRF.</p>

<p>But, again, this has gone on long enough, so I’ll leave myself in suspense until I get around to doing some implementation and checking.
I’ll add a link to a follow-up when I invariably find some of my math errors in this computational testing.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Because that’s what the Stan output calls it. <code class="language-plaintext highlighter-rouge">coda</code> calls it the <a href="https://cran.r-project.org/web/packages/coda/refman/coda.html#gelman.diag"><code class="language-plaintext highlighter-rouge">gelman.diag</code></a>. But there are lots of variables called “R,” and Gelman has proposed other diagnostics<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">9</a></sup>, so I go with “PSRF” in text. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Which drops a few terms from the original, see Remark 1 in <a href="https://www.jstor.org/stable/27286459">Vats and Knudson (2021)</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>Wave your hands and chant something about the (MCMC)CLT. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:9" role="doc-endnote">
      <p>In the end, I think I could have managed without it, but it helped get me on the right track. It’s usually worth going one level deeper of what something <em>is</em> before working with it. <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>Will I regret that some day? Mayhaps. Recording failures can be quite handy later. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>This reminds me why someone much wiser than me told me once to number every equation in a paper. Live and learn. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>In other words, if you’ve got a gnarly starting tree problem, and you use the same tree to initialize all runs, you shouldn’t really use this. Though I’ve seen enough runs go off and fail to converge from the same starting tree to be less paranoid about this than I used to be, as long as the runs are long and the starting tree is more “in the neighborhood of the peak” than “at the peak.” <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>I say “should” because I haven’t absolutely convinced myself this is correct. It feels like it’s right, but when people compute the <a href="https://frazane.github.io/scoringrules/api/energy/">Energy Score</a>, which involves this same average squared difference term disguised as a Norm, it’s not actually what people do. Probably because there are estimates of it that don’t require $\mathcal{O}(n^2)$ comparisons. But still, one wonders. And one wonders what MCMC-induced autocorrelation does to all this. <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Not that I ever immediately remember “diag” is short for “diagnostic” and not “diagonal.” <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[Gelman and Rubin (1992) proposed the Potential Scale Reduction Factor (PSRF), perhaps most commonly know known as “Rhat”1. This is, give or take the field dropping some terms from the original for convenience, perhaps the most widely used way of comparing two or more MCMC chains for diagnosing MCMC convergence. Whidden and Matsen (2015) proposed what they called a “Gelman-Rubin-like” diagnostic as a generalization to phylogenies. Because that’s what the Stan output calls it. coda calls it the gelman.diag. But there are lots of variables called “R,” and Gelman has proposed other diagnostics2, so I go with “PSRF” in text. &#8617; Not that I ever immediately remember “diag” is short for “diagnostic” and not “diagonal.” &#8617;]]></summary></entry><entry><title type="html">A different kind of branching process</title><link href="https://afmagee.github.io/final-size-distributions/" rel="alternate" type="text/html" title="A different kind of branching process" /><published>2026-05-05T00:00:00+00:00</published><updated>2026-05-05T00:00:00+00:00</updated><id>https://afmagee.github.io/final-size-distributions</id><content type="html" xml:base="https://afmagee.github.io/final-size-distributions/"><![CDATA[<p>I remember, 3-4 years ago, starting to hear the term “stuttering chains” tossed around in phylodynamics circles.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>
Loosely, this is about tracking disease spread in the regime where transmission dies out, where phylodynamics is usually about tracking bigger, self-sustaining outbreaks.
More recently, I’ve also dabbled in this regime, in particular in the kinds of models you can use when your data are solely the <a href="https://github.com/cdcgov/nbbp">final sizes of transmission clusters</a>.</p>

<p>The de-facto model of choice here is a negative binomial branching process, so named because every infection causes a number of secondary infections according to a negative binomial distribution.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>
In many ways this is nice: it’s a highly flexible model that includes Geometric branching processes as a special case and Poisson branching processes as a limiting case.
And the math works out nicely to yield useful quantities in closed form.
The probability mass function on final chain sizes exists in closed form, as do the mean and variance of that distribution.</p>

<p>On the other hand, the negative binomial isn’t always exactly the most scrutable distribution.
Sometimes we can get around this by thinking about the negative binomial not in its marginal form, but in its representation as a <a href="https://en.wikipedia.org/wiki/Negative_binomial_distribution#Gamma%E2%80%93Poisson_mixture">mixture of Poissons</a>.
Other times, we still end up stuck with vague notions about relatively higher variance for some parameter values than others.
This is, I think, a common enough feature of continuous mixture distributions: (when) the math works out (it works out) nicely, but discrete mixtures are easier to comprehend.
So, with that in mind, what about a <em>discrete</em> mixture of Poisson distributions for transmission?</p>

<h2 id="our-model">Our model</h2>

<p>Let there be a vector of reproduction numbers $\mathbf{R}$, and associated probabilities $\mathbf{p}$, such that an individual has reproduction number $R_i$ with $p_i$.
Conditional on $R$, then, an individual infects a $\mathrm{Poisson}(R)$ number of people.</p>

<h2 id="where-we-need-to-go">Where we need to go</h2>

<p>Following along with <a href="https://www.nature.com/articles/nature04153">Lloyd-Smith <em>et al</em>. (2005)</a> and <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1002993">Blumberg and Lloyd-Smith (2013)</a>,<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup></p>

<p>For chain size $j$, we have</p>

\[\mathrm{Pr}(j) = \frac{1}{(j - 1)!}T_j^{(j - 1)} \, \Big|_{s = 0}\]

<p>which involves evaluating at $s = 0$ the $(j - 1)$th derivative of</p>

\[T_j(s) = \frac{Q(s)^j}{j}\]

<p>The term $Q(s)$ is the probability generating function of the branching process, which can be represented as</p>

\[Q(s) = \int_0^\infty e^{-u(1 - s)} f(u) \mathrm{d}u\]

<p>where $f(u)$ is the probability distribution function for individual-level $R$.</p>

<h2 id="some-hopefully-mostly-correct-math">Some (hopefully mostly correct) math</h2>

<p>Now we need to backtrack through these steps and hope I don’t go and bodge either the Dirac deltas or derivatives too badly.
To start, I think we get</p>

\[\begin{aligned}
Q(s) &amp;= \int_0^\infty \sum_i e^{-u(1 - s)} p_i \delta(u - R_i) \mathrm{d}u \\
&amp;= \sum_i e^{-R_i(1 - s)} p_i R_i
\end{aligned}\]

<p>This does reassuringly align with the homogenous Poisson case, where there is one $R = R_1$ and one $p_1 = 1$.</p>

<p>Then we can tackle $T_j(s)$</p>

\[T_j(s) = T_j^{(0)}(s) = \frac{\left( \sum_i e^{-R_i(1 - s)} p_i R_i \right)^j}{j}\]

<p>Then, for sanity’s sake letting $x := \sum_i e^{-R_i(1 - s)} p_i R_i$, we can apply the product rule to get,</p>

\[\begin{aligned}
T_j^{(1)}(s) &amp;= \frac{\mathrm{d}}{\mathrm{d}j} \frac{x^j}{j} \\
&amp;= \frac{\mathrm{d}}{\mathrm{d}j} \left( x^j \times j^{-1} \right)\\
&amp;= \frac{x^j \log(x)}{j} - \frac{x^j}{j^2} \\
&amp;= \frac{x^j}{j^2} \left( j \log(x) - 1 \right)
\end{aligned}\]

<p>This is ugly and going to get uglier.
WolframAlpha provides the following,</p>

\[T_j^{(2)}(s) = \frac{x^j}{j^3} \left(j^2 \log^2(x) - 2 j \log(x) + 2 \right)\]

\[T_j^{(3)}(s) = \frac{x^j}{j^4} \left(j^3 \log^3(x) - 3 j^2 \log^2(x) + 6 j \log(x) - 6 \right)\]

\[T_j^{(4)}(s) = \frac{x^j}{j^5} \left(j^4 \log^4(x) - 4 j^3 \log^3(x) + 12 j^2 \log^2(x) - 24 j \log(x) + 24 \right)\]

<p>Clearly there’s a pattern here.
The denominator in the $k$-th order derivative is $j^{k + 1}$.
The term $j^k \log^k(x)$ gets introduced in the $k$-th order, and for order $\ell &gt; k$ becomes scaled by each successive $k$.
The sign in front of this term alternate, starting positive, and thus matching the order.
Generalizing, we get,<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup></p>

\[T_j^{(k)}(s) = \frac{x^j}{j^{k + 1}} \left( \sum_{\ell = 0}^{k} (-1)^{k - \ell} \frac{k!}{\ell!} j^\ell \log^\ell(x) \right)\]

<p>Despite this being ugly, we’re on the home stretch now.
We have</p>

\[\begin{aligned}
\mathrm{Pr}(j) &amp;= \frac{1}{(j - 1)!}T_j^{(j - 1)} \, \Big|_{s = 0}\\
&amp;= \frac{1}{(j - 1)!} \frac{x^j}{j^j} \left( \sum_{\ell = 0}^{j - 1} (-1)^{j - \ell - 1} \frac{(j - 1)!}{\ell!} j^\ell \log^\ell(x) \right)   \Bigg|_{s = 0}\\
&amp;= \frac{x^j}{j^j} \left( \sum_{\ell = 0}^{j - 1} \frac{(-1)^{j - \ell - 1}}{\ell!} j^\ell \log^\ell(x) \right)   \Bigg|_{s = 0}\\
\end{aligned}\]

<p>Finally, removing $x$ and putting back our mixture,</p>

\[\begin{aligned}
\mathrm{Pr}(j) &amp;= \frac{\left[ \sum_i e^{-R_i(1 - s)} p_i R_i \right)^j}{j^j} \left( \sum_{\ell = 0}^{j - 1} \frac{(-1)^{j - \ell - 1}}{\ell!} j^\ell \log^\ell\left( \sum_i e^{-R_i(1 - s)} p_i R_i \right) \right] \,  \Bigg|_{s = 0}\\
&amp;= \frac{\left( \sum_i e^{-R_i} p_i R_i \right)^j}{j^j} \left[ \sum_{\ell = 0}^{j - 1} \frac{(-1)^{j - \ell - 1}}{\ell!} j^\ell \log^\ell\left( \sum_i e^{-R_i} p_i R_i \right) \right]
\end{aligned}\]

<p>This is a horrific PMF.
In good news, for a fixed mixture parameter set $\mathbf{R}, \mathbf{p}$, we can pre-compute the mixture term.
Other than that, we hope we never have to evaluate it for particularly large $j$.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Judging by some of the literature that’s come out since, probably due to <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1002993">Blumberg and Lloyd-Smith (2013)</a> crossing the field barrier. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>A homogenous continuous-time birth-death process, like we use in phylogenetics, is also a negative binomial branching process. Infections last an Exponential duration, given which they infect a Poisson number of additional people. But you get negative binomials marginalizing over <em>Gamma</em> distributions, so we’re in a pretty small corner of parameter space being stuck with exponentials. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>See <a href="https://static-content.springer.com/esm/art%3A10.1038%2Fnature04153/MediaObjects/41586_2005_BFnature04153_MOESM1_ESM.pdf">the supplement</a>, S2.4.1, of Lloyd-Smith for the integral representation of the PGF, and the methods subsection “Size distribution of stuttering chains” for the rest. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>Claude gets this as well. Perhaps reassuringly, it has chosen a slightly sillier representation with a binomial coefficient it has to cancel, rather than the two factorials. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[I remember, 3-4 years ago, starting to hear the term “stuttering chains” tossed around in phylodynamics circles.1 Loosely, this is about tracking disease spread in the regime where transmission dies out, where phylodynamics is usually about tracking bigger, self-sustaining outbreaks. More recently, I’ve also dabbled in this regime, in particular in the kinds of models you can use when your data are solely the final sizes of transmission clusters. Judging by some of the literature that’s come out since, probably due to Blumberg and Lloyd-Smith (2013) crossing the field barrier. &#8617;]]></summary></entry><entry><title type="html">The distribution of the standard deviation of split frequencies</title><link href="https://afmagee.github.io/the-distribution-of-sdsf/" rel="alternate" type="text/html" title="The distribution of the standard deviation of split frequencies" /><published>2026-02-19T00:00:00+00:00</published><updated>2026-02-19T00:00:00+00:00</updated><id>https://afmagee.github.io/the-distribution-of-sdsf</id><content type="html" xml:base="https://afmagee.github.io/the-distribution-of-sdsf/"><![CDATA[<p>Before we dive in too far, it’s worth making sure we know what the standard deviation of split frequencies (SDSF) is.
A split is an edge in a phylogeny, dividing the tips into two sets.
Split probabilities then tell us about support for groupings on the tree.
(We really want clade probabilities, but in unrooted land we have to make sacrifices, and we can apply the same math to clades when we have them.)
Given multiple MCMC chains for a phylogenetic analysis, we can compare split probabilities by looking at their standard deviation across the chains.
Hence, the SDSF.</p>

<p>Let us track the frequency of a split across $m$ chains (each containing $n$ samples), as $f_i$.
The average across all chains is $\bar{f}$.
(These are our per-chain and pooled estimates of the split’s probability.)
The SDSF is either,</p>

\[\text{SDSF} = \sqrt{ \frac{1}{m} \sum_{i=1}^{m} (f_i - \bar{f})^2 }\]

<p>or $\sqrt{m / (m - 1)}$ times that if you want to use the Bessel correction and the unbiased sample SD.</p>

<p>I’ve written in more depth about split frequencies <a href="https://projecteuclid.org/journals/bayesian-analysis/volume-19/issue-2/How-Trustworthy-Is-Your-Tree-Bayesian-Phylogenetic-Effective-Sample-Size/10.1214/22-BA1339.full">here</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, and as with many things in phylogenetics, you can find it in the <a href="https://github.com/NBISweden/MrBayes/blob/develop/doc/manual/Manual_MrBayes_v3.2.pdf">MrBayes manual</a>.</p>

<h2 id="but-what-do-we-do-with-the-sdsf">But what do we do with the SDSF?</h2>
<p>A phylogenetic posterior distribution has many splits.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>
If we want to assess topological convergence, we then have many SDSF to compare.
Commonly, we average them and compare to the arbitrary thresholds of 0.01 and 0.05.
There are many examples of where that’s maybe not great.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>
Some people suggest using the maximum, and this is indeed better.
Often times rare splits are removed because they tend to have low SDSF and thus drag the average down.</p>

<h2 id="defining-bad-better">Defining bad, better</h2>
<p>For a pair of chains, <a href="https://besjournals.onlinelibrary.wiley.com/doi/full/10.1111/2041-210X.13727">Fabreti and Hoehna (2021)</a><sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">4</a></sup> proposed an approach based on the sampling distribution of the absolute difference in split frequencies.
<a href="https://projecteuclid.org/journals/bayesian-analysis/volume-19/issue-2/How-Trustworthy-Is-Your-Tree-Bayesian-Phylogenetic-Effective-Sample-Size/10.1214/22-BA1339.full">I have proposed</a> looking at the <a href="https://onlinelibrary.wiley.com/doi/abs/10.1002/(SICI)1097-0258(19980430)17:8%3C873::AID-SIM779%3E3.0.CO;2-I">distribution of the signed difference</a>.
Either way, the distribution depends on the sample size, which turns out to be a potentially useful degree of freedom.<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">5</a></sup></p>

<p>Fabreti and Hoehna suggested that we define “bad” in terms of absolute differences larger than the 95th percentile of the sampling distribution.
As to the sample size, they fixed it to a threshold ESS, essentially the target ESS that we want our chains to exceed.
Thus, as we run our chains longer, all the absolute differences should come under this threshold.
They specifically suggested computing the exact sampling distribution, and provided this pre-computed for several choices of sample size as it is somewhat computationally intensive for even moderate sample sizes.</p>

<p>I proposed defining “bad” in terms of a difference in frequencies which falls outside a confidence interval.<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">6</a></sup>
I took the sample size to be the effective sample size (of the tree, defining and computing which was the actual point of the paper).
If splits were independent<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">7</a></sup> and we could perfectly estimate the relevant ESS<sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">8</a></sup>, we’d expect this to mean that we’d always have some splits failing the diagnostic.</p>

<h2 id="the-sampling-distribution-of-the-sdsf">The sampling distribution of the SDSF</h2>
<p>Now, neither of those is the SDSF, so why are we talking about them?
Partly to warm up to the idea of sampling distributions involving split frequencies.
Partly to motivate why we might want something different.
Both the signed and absolute difference in split frequencies are natural summaries for comparing two chains.
Neither of them readily generalizes to having three or more chains.
The SDSF does, however, and so it may be a useful starting point to think about generalizations we can use beyond pairwise comparisons.</p>

<p>We could instead think about saying that $m$ chains’ split frequencies are overly divergent if the SDSF is too large, measured by the sampling distribution.
So, how do we get that?</p>

<p>Exact computation is right out.
For two chains and Fabreti and Hoehna’s focal ESS of 625, exact computation requires computing all $625^2$ pairwise probabilities.
That’s nearly 400,000, which is a lot, but not as bad as $625^4$ for 4 chains, which is over 150 billion.</p>

<p>We’re left with approximation, so let’s do some math and get to something we know how to approximate.<sup id="fnref:10" role="doc-noteref"><a href="#fn:10" class="footnote" rel="footnote">9</a></sup>
For the moment, let us forget that we’re working with MCMC samples, and just think about $m$ samples of size $n$.
Under the null (that all chains are sampling the same split frequency), the distribution of <em>counts</em> of times we see the split in any chain is identical, and all chains are independent.
Substituting $\bar{f}$ for the true mean, the count in chain $i$ is $n f_i$ which has mean $n \bar{f}$.
What is the variance?
If the samples in each chain are independent (as we typically assume in convergence diagnostics), it is $n \bar{f} (1 - \bar{f})$.
Therefore, we can turn to our old friend the central limit theorem to assert that,</p>

\[\frac{n f_i - n \bar{f}}{\sqrt{n \bar{f} (1 - \bar{f})}}\]

<p>is asymptotically Normal(0,1).</p>

<p>Armed with this,</p>

\[\begin{align*}
\text{SDSF} &amp;= \sqrt{ \frac{1}{m} \sum_{i=1}^{m} (f_i - \bar{f})^2 }\\
&amp;= \sqrt{ \frac{1}{m} \sum_{i=1}^{m} \frac{(n f_i - n \bar{f})^2}{n^2} }\\
&amp;= \sqrt{ \frac{\bar{f} (1 - \bar{f})}{mn} \sum_{i=1}^{m} \frac{(n f_i - n \bar{f})^2}{n \bar{f} (1 - \bar{f})} }
\end{align*}\]

<p>We can see that the term in the summand is the square of a variable which is asymptotically Normal.
Therefore the sum is asymptotically $\chi^2$ with $m - 1$ degrees of freedom.<sup id="fnref:11" role="doc-noteref"><a href="#fn:11" class="footnote" rel="footnote">10</a></sup>
We lose a degree of freedom because the average, along with any $m - 1$ of the squared deviations, determines the remaining one.</p>

<p>However, we’re not <em>quite</em> done yet, because we actually have the square root of a scaled $\chi^2$ variable.
The scaling is easy enough.
A $\chi^2(m)$ distribution is a Gamma(shape = $m/2$, scale = $2$) distribution, and <a href="https://en.wikipedia.org/wiki/Gamma_distribution#Scaling">we can scale those by, well, scaling the scale parameter</a>.
So the term inside the square root is a Gamma($m / 2$, $2 (\bar{f} (1 - \bar{f})) / mn$).
The square root of a Gamma-distributed variable turns out to be known as a <a href="https://en.wikipedia.org/wiki/Nakagami_distribution">Nakagami distribution</a>.</p>

<p>Thus, for $m$ sets of $n$ independent samples, the distribution of the SDSF is asymptotically Nakagami with shape parameter $(m - 1) / 2$, and spread parameter<sup id="fnref:12" role="doc-noteref"><a href="#fn:12" class="footnote" rel="footnote">11</a></sup> $(\bar{f} (1 - \bar{f})) / n$.
Or, if you want the Bessel version, shape parameter $(m - 1) / 2$ and spread parameter $(\bar{f} (1 - \bar{f}) (m - 1)) / mn$.</p>

<h3 id="reckoning-with-mcmc">Reckoning with MCMC</h3>

<p>The actual amount that $f_i$ varies from $\bar{f}$ depends not on the number of samples we take, but the effective sample size of (that split in) chain $i$.
If we simply wish to generalize the approach of Fabreti and Hoehna to more than two chains, this isn’t a deal breaker, we simply put the target ESS (e.g. 625) in for $n$.
Then, as our actual ESS gets large enough, all of the SDSF will eventually decrease below the 95th percentile of their specified Nakagami distributions.</p>

<p>On the other hand, if we want to use this to generalize confidence interval-based detection of abnormally deviant splits, we have to reckon with the fact that each chain has its own effective sample size.
That is, there is not really one $n$ but there are actually $m$, one $n_i$ per chain.</p>

<p>Mathematically, we can do this just fine.
The derivation is very similar, as the logic is otherwise identical.</p>

\[\begin{align*}
\text{SDSF} &amp;= \sqrt{ \frac{1}{m} \sum_{i=1}^{m} (f_i - \bar{f})^2 }\\
&amp;= \sqrt{ \sum_{i=1}^{m} \left( \frac{1}{m} \right) \frac{(n_i f_i - n_i \bar{f})^2}{n_i^2} }\\
&amp;= \sqrt{ \sum_{i=1}^{m} \left( \frac{\bar{f} (1 - \bar{f})}{mn_i} \right) \frac{(n_i f_i - n_i \bar{f})^2}{n_i \bar{f} (1 - \bar{f})} }
\end{align*}\]

<p>This is the square root of the sum of $m$ independent scaled $\chi^2(1)$ distributions.
A $\chi^2(1)$ distribution is a Gamma($1/2$, $2$) distribution, and while each scaling is different, each term in the sum is Gamma($1/2$, $2 \bar{f} (1 - \bar{f}) / mn_i$).
Unfortunately, while Gammas with the same scale parameter can be summed, Gamma distributions with the same shape parameter but different scales cannot.</p>

<p>Are we out of luck?
Sort of.
Still, we’ve come this far, and there are at least a handful of options which present themselves.</p>

<h4 id="turtles-all-the-way-down">Turtles all the way down</h4>

<p>We are already approximating things, so what’s one more, we might ask?
The potentially least-approximate approximation is brute force simulation.
But as we would have to, for some large-ish number of replicates, simulate $m$ Gammas for each split, this quickly becomes intractable.
So, we could do something more approximate,<sup id="fnref:13" role="doc-noteref"><a href="#fn:13" class="footnote" rel="footnote">12</a></sup> but it if we don’t find an astonishingly good one it seems unlikely to produce a particularly performant result.<sup id="fnref:14" role="doc-noteref"><a href="#fn:14" class="footnote" rel="footnote">13</a></sup></p>

<p>What would be more approximate?
We could use $\min(n_i)$ for all $n_i$ and proceed with the equal sample sizes case to aim for a lenient test.
We could use $\max(n_i)$ instead for a strict test.
If the ESS doesn’t vary too much across chains, then we might not do terribly by using the mean instead.<sup id="fnref:15" role="doc-noteref"><a href="#fn:15" class="footnote" rel="footnote">14</a></sup>
Lastly, we could try to match variances.
That is, each term inside the sum has variance $2 \left(\bar{f} (1 - \bar{f}) / mn_i \right)^2$, and, since chains are independent,<sup id="fnref:16" role="doc-noteref"><a href="#fn:16" class="footnote" rel="footnote">15</a></sup> the variance of the sum is the sum of the variances.
So, if we assume the sum looks Gamma-ish, with shape parameter $(m - 1)/2$, the scale parameter is $\sqrt{2 \sigma^2 / (m - 1)}$.</p>

<p>I don’t get the sense that any of these are particularly good options unless the ESS are pretty consistent across the chains.</p>

<h4 id="reframe-the-problem">Reframe the problem</h4>

<p>We could also reframe the problem.
We’ve been asking about the sampling distribution of the SDSF.
But what if we asked about the sampling distribution of a related but rescaled and thus slightly different quantity?</p>

\[\begin{align*}
\text{rSDSF} &amp;= \sqrt{ \frac{1}{m} \sum_{i=1}^{m} (f_i - \bar{f})^2 \left( \frac{\bar{f} (1 - \bar{f})}{n_i} \right)^{-1}}\\
&amp;= \sqrt{ \frac{1}{m} \sum_{i=1}^{m} \frac{(n_i f_i - n_i \bar{f})^2}{n_i \bar{f} (1 - \bar{f})} }
\end{align*}\]

<p>If we rescale the squared deviations of split frequencies, we can remove the sample size dependency.
Thus, as in the equal sample sizes case, we get a tractable Nakagami distribution.
One might note that, in essence, we’ve cheated, and changed the summary statistic to counts of splits.
The important thing to note is that these aren’t the actual counts of times we’ve seen any given split in any given chain, though.
Since the $n_i$ are the per-chain ESS, these are hypothetical counts.</p>

<p>One potential advantage here is that, since all splits will end up with the same (asymptotic) distribution of the rSDSF, we can look at the overall performance of the chain in a single summary statistic.</p>

<h2 id="fin">Fin</h2>

<p>How well any of this works, if indeed it works at all, I leave for future work.
I should note, following along from the final cheat to working with split counts, instead of frequencies, that there’s a somewhat conceptually simpler option presenting itself here.
If we correct for the fact that each chain has a different effective sample size, we can simply use a chi-squared test on the counts of samples with (and without) the split in each chain.
We could also use a chi-squared test in the Fabretti Hoehna style, fixing the target ESS and letting the observed test statistics decrease below it as we add more samples.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>It’s my party, and I’ll self-cite if I want to. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Sometimes many, many, many. Sometimes an absurd number. Sometimes more than that. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>In <a href="https://academic.oup.com/sysbio/article/69/2/209/5555782">this paper</a>, we use a stricter summary and even still values of 0.03 aren’t great. Figure S11 of <a href="https://projecteuclid.org/journals/bayesian-analysis/volume-19/issue-2/How-Trustworthy-Is-Your-Tree-Bayesian-Phylogenetic-Effective-Sample-Size/10.1214/22-BA1339.full">this paper</a> is a real doozy.<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">16</a></sup> <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>Well I guess I can cite other people. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>But not <em>that</em> sort. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>Yes, I used the 95% CI in practice. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>They’re not. Minimally because many splits are incompatible with each other and so the presence of one means the absence of the others <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:9" role="doc-endnote">
      <p>We appear to be able to do far better than we have any right to expect, but ESS is still an estimator. <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:10" role="doc-endnote">
      <p>10 internet points if you can guess where this is headed already. <a href="#fnref:10" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:11" role="doc-endnote">
      <p>Good job to everyone who saw this coming. <a href="#fnref:11" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:12" role="doc-endnote">
      <p>Yes it’s an awkward name for a parameter. <a href="#fnref:12" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:13" role="doc-endnote">
      <p>It took me a very long time to figure out why the favored language when describing MCMC was “we approximate the posterior via…” And it’s the same reason I have to say that this is an approximation: even when MCMC works, and actually samples from the posterior, that’s all we have, <em>samples</em>. We don’t have the distribution itself. So, while we could get a pretty good idea of the posterior mean or 95th quantile, it’s still an approximation to the true mean or quantile. And to get a good approximation to quantiles out in the tail, we need a lot of samples. Far more than common the sort of ESS you commonly see in phylogenetics. <a href="#fnref:13" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:14" role="doc-endnote">
      <p>The approximation for all sample sizes equal is not bad. But in limited testing it appears to be outperformed by a more standard chi-squared approximation using a contingency table of the presence/absence of the split across all chains. So a further approximation is unlikely to be outstandingly performant. <a href="#fnref:14" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:15" role="doc-endnote">
      <p>It seems plausible, if not indeed likely, that all of these approximations would only work reasonably in the case where the ESS are all relatively similar, anyways. <a href="#fnref:15" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:16" role="doc-endnote">
      <p>The more I work with MCMC convergence math, the more I get why this gets put in like a mantra in theoretical papers about MCMC. In practice, we violate this all the time because initializing good trees is hard. We need some way to have our cake and eat it too, but I don’t think it’s been invented yet. <a href="#fnref:16" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>Yes there are good examples in other peoples’ work. But I know (some of) what I’ve written so these were handy. Besides, the crusade against self-citation is overly broad. Wanton self-citation is one thing, as is citing yourself to the exclusion of other relevant work. But it is not, by itself, a bad thing. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[Before we dive in too far, it’s worth making sure we know what the standard deviation of split frequencies (SDSF) is. A split is an edge in a phylogeny, dividing the tips into two sets. Split probabilities then tell us about support for groupings on the tree. (We really want clade probabilities, but in unrooted land we have to make sacrifices, and we can apply the same math to clades when we have them.) Given multiple MCMC chains for a phylogenetic analysis, we can compare split probabilities by looking at their standard deviation across the chains. Hence, the SDSF.]]></summary></entry><entry><title type="html">Multifurcating birth death models</title><link href="https://afmagee.github.io/multibirth/" rel="alternate" type="text/html" title="Multifurcating birth death models" /><published>2026-01-25T00:00:00+00:00</published><updated>2026-01-25T00:00:00+00:00</updated><id>https://afmagee.github.io/multibirth</id><content type="html" xml:base="https://afmagee.github.io/multibirth/"><![CDATA[<p>Branching process models for phylogenies that allow for multifurcating trees, and not just binary ones, have been in the literature for some time.
You have your $\Lambda$-coalescents and your <a href="https://arxiv.org/pdf/2407.14976">Beta-coalescents</a><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> and such, where you hear tale of heavy-tailed offspring distributions and how those can lead to such distributions.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>
I’m going to stay out of population genetic theory and in the realm of phylodynamics, where I go back and forth on this topic.
Are there ever really <em>simultaneous</em> infections?
Perhaps not, but maybe close enough on the time scales our trees take up.
Though would we ever see them?
I’m certainly not used to datasets where we sample infections that densely, but it could still leave signatures we care about in the tree.</p>

<p>I like a good forward-time generative model, and the other day I stumbled across some pieces that make me think it could actually be pretty easy to get tractable<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> birth-death phylodynamic models with multi-births.
But to get there, we need some background.</p>

<h2 id="the-short-ish-version">The short-ish version</h2>

<p>Deriving the likelihood function for a phylogenetic birth-death model more or less amounts to figuring out how two quantities change through time.
I’ve called them <a href="https://www.biorxiv.org/content/10.1101/2021.01.14.426715v1.full.pdf">$D(t)$ and $E(t)$ before</a>, but others call them <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1003919">$q(t)$ and $p_0(t)$</a>.
I’ll stick with D and E, and note that the lack of subscripts implies that we’re restricting ourselves to models where all lineages alive at some time $t$ are exchangeable.
So, time-dependent birth-death models yes, clade-dependent models no.</p>

<p>I like to think of $D(t)$ as the probability of straight lines in the observed phylogeny.
Slightly more rigorously, it’s the probability of a single lineage surviving from time $t$ (measured in time before the present) to 0 (the present).<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>
For a lineage surviving from some time $t_o$ to some more recent $t_y$, the probability is $D(t_o) / D(t_y)$.
Thus, the birth-death likelihood of a tree can be expressed as a product of $D(t_o) / D(t_y)$ terms, plus terms for the observed births and samples taken.
In turn, $D(t)$ depends on $E(t)$, the probability that a lineage alive at time $t$ leaves no sampled descendants in our tree.</p>

<p>To actually get anywhere with a birth-death model derivation, we start by thinking about $\mathrm{d}E/\mathrm{d}t$ and how to integrate it.
Then we think about $\mathrm{d}D/\mathrm{d}t$ and how to integrate it.
Then we sprinkle in the probabilities of births and other events we can see, namely births and sampling events.
We can ignore sampling, which can get complicated, and we’ll get to the births later.</p>

<h2 id="what-are-we-talking-about-changing">What are we talking about changing?</h2>

<p>Before we look at how each of these changes if we allow for multiple births, we need to be clear what we mean.
In a typical birth-death model, there’s a birth rate $\lambda(t)$ at which one lineage gives birth to another.
I’m suggesting our model instead says that there’s a rate $\lambda(t)$ at which one lineage gives birth to <em>some number</em> of additional lineages.
That is, to simulate forward, when your process says that the next event is a birth in lineage $\ell$, you draw the number of children from some distribution, $\nu \sim \mathrm{Pr(\nu)}$.</p>

<h2 id="et">$E(t)$</h2>

<p>I prefer to think about $E(t + \Delta t)$ to $\mathrm{d}E/\mathrm{d}t$.
That is, in a sufficiently small window of time, how does the extinction probability change?
Then you can rearrange and divide through and get the differential needed.</p>

<p>Now I’m going to add novel notation in the possibly misguided hopes that it will help abstract away from model details.
Let’s call $\mathcal{E}$ be the event that the lineage goes extinct, $\mathcal{N}$ the event that nothing happens to it, $\mathcal{B}$<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup> the event that it experiences a birth event.
Then let $\mathrm{Pr}(\mathcal{E}| t, \Delta t)$ the probability that it goes extinct between $t$ and $t + \Delta t$.</p>

<p>Extinction probabilities are propagated according to equations of the form</p>

\[E(t + \Delta t) = \mathrm{Pr}(\mathcal{N}| t, \Delta t) E(t) + \mathrm{Pr}(\mathcal{E}| t, \Delta t) + \mathrm{Pr}(\mathcal{B}| t, \Delta t) E(t)^2\]

<p>These correspond to the three things that can happen in the interval: absolutely nothing, it goes extinct, or there’s a birth.
If nothing happens, then it still has to go extinct eventually, hence the $E(t)$ in that term.
If it goes extinct, end of story, extinction achieved.
And if there’s a birth, both lineages have to then go extinct, hence the $E(t)^2$.</p>

<p>If we want to allow multi-births, only this last term changes, because now there are many possibilities: a birth event with 1 child, 2 children, and so on.
All of these will eventually have to go extinct, so the equation becomes</p>

\[E(t + \Delta t) = \mathrm{Pr}(\mathcal{N}| t, \Delta t) E(t) + \mathrm{Pr}(\mathcal{E}| t, \Delta t) + \mathrm{Pr}(\mathcal{B}| t, \Delta t) E(t) \left[ \sum_{\nu} E(t)^\nu \mathrm{Pr}(\nu) \right]\]

<p>So, if we want this to work and be tractable, we need a $\mathrm{Pr}(\nu)$ that plays nicely with this functional form and produces an analytical solution.
Because if it doesn’t (even approximately), we can’t get at one of the most important quantities in our likelihood.</p>

<p>Also, let’s be careful, because I have now done the math assuming that at a birth event one lineage survives, and some number $\nu$ of <em>additional</em> lineages arise.
We could assume that the original one dies and then $1 + \nu$ lineages are born, but this formulation should play better with PMFs of most distributions, so if we want to think that way, we should do the math this way, and then reinterpret later.</p>

<h3 id="a-distribution-is-just-a-sequence-with-partial-sums-we-know">A distribution is just a sequence with partial sums we know<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup></h3>

<p>What would play nicely with a sum of this form?
The <a href="https://en.wikipedia.org/wiki/Geometric_distribution">geometric distribution</a> seems a likely candidate, with parameter $\theta \in [0,1]$ and random variable $\nu$ the PMF is</p>

\[\mathrm{Pr}(\nu) = \theta(1 - \theta)^{\nu - 1}\]

<p>Where the Geometric goes, typically goes the <a href="https://en.wikipedia.org/wiki/Negative_binomial_distribution">Negative Binomial</a>, of which it is a special case.
Adding parameter $r$, and shopping around for the <a href="https://en.wikipedia.org/wiki/Negative_binomial_distribution#Alternative_formulations">right parameterization</a> we get,</p>

\[\mathrm{Pr}(\nu) = {\nu + r - 1 \choose \nu} \theta^\nu(1 - \theta)^r\]

<p>There’s also the <a href="https://en.wikipedia.org/wiki/Poisson_distribution">Poisson distribution</a> (which allows any $\theta &gt; 0$),</p>

\[\mathrm{Pr}(\nu) = e^{-\theta} \frac{\theta^\nu}{\nu!}\]

<p>A final candidate is the <a href="https://en.wikipedia.org/wiki/Logarithmic_distribution">Logarithmic distribution</a>, for $\theta \in (0,1)$</p>

\[\mathrm{Pr}(\nu) = \frac{-1}{\ln(1 - \theta)} \frac{\theta^\nu}{\nu}\]

<p>It was the Logarithmic I was staring at when this whole idea really took shape.
It shows up in compound Poisson contexts, which is a reason to be hopeful it might work here.
That it naturally lives on $\nu \geq 1$ instead of $\nu \geq 0$ may or may not have much utility, though it would be nice to not have to work with conditional PMFs.
The Geometric can, depending on how you define write it, live on either space; I’ve written it above for $\nu \geq 1$.
The Negative Binomial rears its head in a lot of places, though often as a <em>result</em> of mucking with probability distributions.
In this case, the Geometric looks more plausible, since it’s already off by a $1 - \theta$ from the form we want, and the Negative Binomial just keeps going that way.</p>

<h2 id="dt">$D(t)$</h2>

<p>Propagating $D(t)$ looks similar.
In the typical case,</p>

\[D(t + \Delta t) = \mathrm{Pr}(\mathcal{N}| t, \Delta t) D(t) + 2 \mathrm{Pr}(\mathcal{B}| t, \Delta t) D(t) E(t)\]

<p>This corresponds to two possibilities.
Either nothing happens in the interval, so the straight line we see in the tree was what really happened in that time, or there’s a birth where all the other lineages go extinct, and the straight line we see is simply all that’s left.
I think we can interpret the 2 as ${2 \choose 1}$, that is, pick one of the two lineages to survive, as we can’t distinguish them.</p>

<p>The generalization would then be,</p>

\[D(t + \Delta t) = \mathrm{Pr}(\mathcal{N}| t, \Delta t) D(t) + \mathrm{Pr}(\mathcal{B}| t, \Delta t) D(t) \left[ \sum_{\nu} {\nu \choose 1} E(t)^\nu \mathrm{Pr}(\nu) \right]\]

<p>Does this tell us anything more about distributions that might work for $\mathrm{Pr}(\nu)$?
Maybe.
The ${\nu \choose 1}$ term looks a bit awkward being added to the Geometric, and turns the $1 / \nu!$ in the Poisson into a $1 / (\nu - 1)!$.
Both of those tilt the sequences in ways that seem less than promising to make the math work out.
On the other hand, it simply cancels the Logarithmic’s $1 / \nu$ in the denominator, leaving a cascading sum of $(E(t) \theta)^\nu$ terms, which ought to sum up nicely.<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup></p>

<h2 id="lambdat">$\lambda(t)$</h2>

<p>I was about to start doing the math,<sup id="fnref:7:1" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup> when I realized I had missed something.
In a typical birth-death model, the contribution of the births observed at times $\mathbf{t}$ to the likelihood is very straightforwardly the product of the birth rate at all times of observed births</p>

\[\prod_i \lambda(t_i)\]

<p>But we have undone the stipulations that make it this straightforward.
Now it’s not that every birth leads to two lineages, both of which we see.
Now every birth even leads to some number of children, some (usually) lesser number of which we see.
That last bit smuggles in the extinction probability.
Say we see a trifurcation in the tree: were there two offspring lineages, and we’re seeing all, or might there have been eight?<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">8</a></sup></p>

<p>Let there be $\nu$ lineages descending from the birth event in the tree, and $\nu_\mathcal{E}$ be the number of unobserved extinct lineages, then the likelihood ought to be</p>

\[\prod_i \lambda(t_i) \left[ \sum_{\nu_\mathcal{E} \geq 0} {\nu + \nu_\mathcal{E} \choose \nu} E(t_i)^{\nu_\mathcal{E}} \mathrm{Pr}(\nu + \nu_\mathcal{E}) \right]\]

<h2 id="attempting-mathematics">Attempting mathematics</h2>

<p>With all that said and done, and having written far more than I expected to by this point, I am now going to try actually making this work.
I don’t think I’m going to actually finish the math, but I’m going to squint at things and see if I think there’s hope for finishing it up later.
I think the thing to do is start with the sum that looks the hardest, and work our way towards easier ones.
Without showing my work, I’ll say I tried the Logarithmic and gave up.
The $1 / \nu$ in the PMF reacted poorly with the combinatorial coefficient in the probabilities of birth events, leaving a stray random variable lying about.</p>

<h3 id="lambdat-revisited">$\lambda(t)$ revisited</h3>

<p>Replacing $E(t)$ with $E$ to reduce clutter, we want the following to work out to something nice</p>

\[\sum_{\nu_\mathcal{E} \geq 0} {\nu + \nu_\mathcal{E} \choose \nu} E^{\nu_\mathcal{E}} \theta (1 - \theta)^{(\nu + \nu_\mathcal{E}) - 1}\]

\[\sum_{\nu_\mathcal{E} \geq 0} {\nu + \nu_\mathcal{E} \choose \nu} E^{\nu_\mathcal{E}} \theta (1 - \theta)^{\nu_\mathcal{E}} (1 - \theta)^{\nu - 1}\]

\[\theta (1 - \theta)^{\nu - 1} \sum_{\nu_\mathcal{E} \geq 0} {\nu + \nu_\mathcal{E} \choose \nu} E^{\nu_\mathcal{E}} (1 - \theta)^{\nu_\mathcal{E}}\]

<p>Define $p := E (1 - \theta)$</p>

\[\frac{\theta (1 - \theta)^{\nu - 1}}{(1 - p)^{\nu + 1}} \sum_{\nu_\mathcal{E} \geq 0} {\nu + \nu_\mathcal{E} \choose \nu} p^{\nu_\mathcal{E}} (1 - p)^{\nu + 1}\]

<p>Note that</p>

\[{\nu + \nu_\mathcal{E} \choose \nu} = \frac{(\nu + \nu_\mathcal{E})!}{\nu! \nu_\mathcal{E}!} = {\nu + \nu_\mathcal{E} \choose \nu_\mathcal{E}}\]

<p>Ergo</p>

\[\frac{\theta (1 - \theta)^{\nu - 1}}{(1 - p)^{\nu + 1}} \sum_{\nu_\mathcal{E} \geq 0} {\nu + \nu_\mathcal{E} \choose \nu_\mathcal{E}} p^{\nu_\mathcal{E}} (1 - p)^{\nu + 1}\]

<p>We can now recognize the summand as the PMF of a $\mathrm{NegativeBinomial}(\nu_\mathcal{E}; \nu + 1, p)$ random variable, which means it evaluates to 1.
Before we even think about cleaning it up, we need to see that the other terms work out.</p>

<h3 id="dt-revisited">$D(t)$ revisited</h3>

<p>We need this sum to work out too</p>

\[\sum_{\nu \geq 1} \nu E^\nu \theta (1 - \theta)^{\nu - 1}\]

\[\frac{\theta}{1 - \theta} \sum_{\nu \geq 1} \nu E^\nu (1 - \theta)^{\nu}\]

<p>Define $(1 - p) := E (1 - \theta)$</p>

\[\frac{\theta}{p(1 - \theta)} \sum_{\nu \geq 1} \nu (1 - p)^\nu p\]

<p>We can expand the summand to include $\nu = 0$ where it is 0,</p>

\[\frac{\theta}{p(1 - \theta)} \sum_{\nu \geq 0} \nu (1 - p)^\nu p\]

<p>And now we can recognize the sum as the expectation of a $\mathrm{Geometric}(\nu; p)$-distributed random variable,<sup id="fnref:10" role="doc-noteref"><a href="#fn:10" class="footnote" rel="footnote">9</a></sup> but the 0-including kind of Geometric, so we can simplify to</p>

\[\frac{\theta}{p(1 - \theta)} \frac{1 - p}{p} = \frac{\theta (1 - p)}{p^2(1 - \theta)}\]

<h3 id="et-revisited">$E(t)$ revisited</h3>

<p>The last, though perhaps most important, thing that we need to work out nicely is</p>

\[\sum_{\nu \geq 1} E(t)^\nu \theta (1 - \theta)^{\nu - 1}\]

\[\frac{\theta}{(1 - \theta)} \sum_{\nu \geq 1} E(t)^\nu (1 - \theta)^{\nu}\]

<p>Using $p := E (1 - \theta)$ again</p>

\[\frac{\theta}{(1 - \theta)} \sum_{\nu \geq 1} p^\nu\]

<p>This is everyone’s favorite geometric sequence, with a sum of $p / (1 - p)$, yielding</p>

\[\frac{\theta p}{(1 - \theta) (1 - p)}\]

<h2 id="so-where-are-we">So where are we?</h2>

<p>I haven’t checked my math sufficiently to be sure I’m right, but I think even with some inevitable slips, there will be an analytical form for all three of the terms we’ve looked at.
Which means that there are analytical solutions to $D(t + \Delta t)$ and $E(t + \Delta t)$, and a derivation of an analytically tractable multi-birth-death model is possible.
The details of actually making the math work out will depend on the model itself, and I’d be shocked if something didn’t get ugly.<sup id="fnref:11" role="doc-noteref"><a href="#fn:11" class="footnote" rel="footnote">10</a></sup>
But it looks possible to me.</p>

<p>This model could be useful in phylodynamics when sampling is sufficiently good to actually see superspreading in a tree, or perhaps when sampling isn’t quite as good, but there’s still more heterogeneity in offspring distribution than a usual time-varying birth-death model.<sup id="fnref:12" role="doc-noteref"><a href="#fn:12" class="footnote" rel="footnote">11</a></sup>
Or it could be useless.
But it might be fun to find out.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Which, just for fun, <em>are</em> $\Lambda$ coalescents. And for extra fun, write out Beta. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>To their credit, <a href="https://arxiv.org/pdf/2407.14976">Zhang and Palacios (2024)</a>, who I linked to, also discuss in a somewhat more concrete way, places we might want models of this ilk. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Speculative, simulation-only models have their place. But I like models I can fit to data, and I like likelihoods. Yes, I’m a hopelessly basic Bayesian at times. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>There are alternative ways to define this. You’ll often find the p/q notation with a time endpoint other than the present. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>I’ll include in here the “and only a birth” stipulation of the usual “there’s only one event in this window” assumptions. E.g., you can have a multi-birth but not a birth and a death. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>You can get far in probability by being able to do math. But you can get farther faster by being able to skip the math and say “well, that’s just the kernel of a squingleforth distribution, and thus we know…” <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>Yes I could just start doing the math, but I won’t. Math is hard and I want some notion I’m barking up the right tree before I get started. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:7:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>Actually, for that matter, are we seeing the original lineage and two children, or three descendants? Does it matter? I think that, given our assumptions about lineages being exchangeable, it’s all the same thing. But I think one would very much have to be careful when taking this model and trying to BiSSE-fy it, allowing lineage-dependency. Because I’m about to make good on the assumption that I can toss around a bunch of equivalent $E(t)$ terms for a bunch of lineages coming out of the birth event, and that might not be true. As always, caveat generalizator.<sup id="fnref:9" role="doc-noteref"><a href="#fn:9" class="footnote" rel="footnote">12</a></sup> <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:10" role="doc-endnote">
      <p>Sure, we could have just tackled the sum directly. And I was thinking about it, but then I saw the resemblance and saved myself all that effort. <a href="#fnref:10" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:11" role="doc-endnote">
      <p>Well, uglier, anyways. Some time-varying phylogenetic birth-death model likelihoods are real doozies. <a href="#fnref:11" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:12" role="doc-endnote">
      <p>We could also just adopt a multi-type birth-death model where birth rates for individuals are drawn randomly from some distribution. But that’s likely to end up going down the road of a BiSSE-style likelihood, and those require numerical integration, which it looks like we might be able to avoid here. <a href="#fnref:12" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:9" role="doc-endnote">
      <p>I’m pretty sure that that’s pig latin. It’s my blog and I’ll get things wrong if I want to. <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[Branching process models for phylogenies that allow for multifurcating trees, and not just binary ones, have been in the literature for some time. You have your $\Lambda$-coalescents and your Beta-coalescents1 and such, where you hear tale of heavy-tailed offspring distributions and how those can lead to such distributions.2 I’m going to stay out of population genetic theory and in the realm of phylodynamics, where I go back and forth on this topic. Are there ever really simultaneous infections? Perhaps not, but maybe close enough on the time scales our trees take up. Though would we ever see them? I’m certainly not used to datasets where we sample infections that densely, but it could still leave signatures we care about in the tree. Which, just for fun, are $\Lambda$ coalescents. And for extra fun, write out Beta. &#8617; To their credit, Zhang and Palacios (2024), who I linked to, also discuss in a somewhat more concrete way, places we might want models of this ilk. &#8617;]]></summary></entry><entry><title type="html">Can you bootstrap that?</title><link href="https://afmagee.github.io/can-you-bootstrap-that/" rel="alternate" type="text/html" title="Can you bootstrap that?" /><published>2026-01-08T00:00:00+00:00</published><updated>2026-01-08T00:00:00+00:00</updated><id>https://afmagee.github.io/can-you-bootstrap-that</id><content type="html" xml:base="https://afmagee.github.io/can-you-bootstrap-that/"><![CDATA[<p>I like a good crazy idea<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.
I also, clearly, have a problem with answers in search of questions<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.
In “a little bit of column A, a lot of column B”: bootstrapping branching times in a time-calibrated phylogeny.
This is only a little crazy, because in time-varying phylogenetic birth-death models where all taxa are sampled at the present, <a href="https://academic.oup.com/bioinformatics/article-lookup/doi/10.1093/bioinformatics/btt153">divergence times are IID.</a>.
Why is it an answer in search of a solution?
Let’s take a look.</p>

<h2 id="the-good">The good</h2>

<p>The good news, such as it is, is that nonparametric bootstrapping of divergence times does seem to work.
I compared it to the parametric bootstrap, which is a similarly widely-scoped hammer for the problem.
As you can see, they both achieve basically the correct coverage in a simple constant-rate birth-death model scenario.</p>

<p><img src="../assets/images/posts/2026-01-08_bt_bootstrap_coverage.png" alt="Coverage of birth and death rates from a constant-rate birth-death model simulation and analysis of trees of 100 tips." /></p>

<p>In general, the nonparametric bootstrap is probably a bit faster than the parametric bootstrap.
Univariate likelihood profiling could perhaps be faster still, but, well, it’s univariate<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.
<a href="#code-appendix">As tested</a>, I used <a href="https://github.com/hoehna/TESS">TESS</a>, which is very fast for simulating birth-death processes, for the parametric bootstrap, and R’s built-in <code class="language-plaintext highlighter-rouge">sample()</code> for the nonparametric bootstrap.
TESS numerically integrates the CDF of divergence times and then draws from that CDF.
This is, minimally, one more step than simply drawing the indices of the samples for the nonparametric bootstrap.
The number of random numbers needed in both case is the same (other simulation-based approaches would need more).
I can’t entirely explain why, in practice, with 250 bootstrap replicates the nonparametric bootstrap was nearly two times faster than the parametric.
It could just be how much effort has gone into R’s <code class="language-plaintext highlighter-rouge">sample()</code> over the years, or that drawing from a numerically-approximated CDF inherently requires more comparisons than drawing uniform random integers.</p>

<h2 id="the-bad">The bad</h2>

<p>Nonparametric bootstrapping of divergence times, as an approach, is actually rather limited in application scope.
That might sound weird, given how few assumptions it would seem to rely on.
But the assumptions it does rest on are not small.</p>

<p>The explicit assumption is that branching times are IID.
I won’t spend too much ink on this, as it is the other, implicit, assumption that is the real death blow.
I do think we can push past models with tips only at the present<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>, and serially-sampled birth death models are important.
I do not have a strong intuition on whether times still be IID if we think there are, say, arbitrary waiting time distributions for births and deaths.
Quite possibly?</p>

<p>The implicit assumption here is that branching times are all we care about.
After all, that’s all we’re bootstrapping.
But when birth and/or death rates change along the tree, it’s the tree that matters, not just the divergence times.
That means trait-dependent models are out, or even just models that let the birth and death rates vary from branch to branch.
Such SSE family models comprise a huge portion of the usefulness of phylogenetic birth-death models<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup> in practice.</p>

<p>It’s also worth noting that the nonparametric bootstrap is still an asymptotic approach.
So it’s not liable to be particularly helpful if you only have a tree with ten tips.</p>

<h2 id="the-ugly">The ugly</h2>

<p>Nonparametric bootstrapping of divergence times is completely unnecessary in practice.
Roughly, I’d say, the nonparametric bootstrap is useful when it allows us to do something we could not otherwise do satisfactorily.
Many times this is about our ability to remove assumptions from the model.
Other times this is simply that we have no other way to address uncertainty about our estimates.</p>

<p>I can’t see ways that this allows us to remove assumptions from our modeling.
In other statistical arenas, for example, it allows us to quantify uncertainty about sample means, or to compare multiple means, without assuming a distribution.
But in phylogenetic contexts, while we may not adore our branching models, we’re far more interested in their parameters (we care a lot about things that increase the branching rate) than their sample statistics.
And we can’t even address all sample statistics with this approach: we could talk about the mean branching time (which I cannot, for the life of me, fathom a utility for) but not the number of branching events (which, under the right circumstances, we might just care about<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup>).</p>

<p>And for the class of models to which bootstrapping divergence times is applicable, I don’t think we have any trouble getting at uncertainty.
Lots of our models have tractable likelihoods, so we can always use Bayesian inference to get at (multivariate) parameter uncertainty.
And I’d argue that most of our models are generative, so even if inference isn’t straightforward, we can simulate from them and use a parametric bootstrap.</p>

<h2 id="tldr">TL;DR</h2>

<p>Nonparametric bootstrapping of branching times is doable, under strong assumptions, but not particularly useful, unless you happen to have a purely time-dependent model which is not generative and which lacks a tractable likelihood.</p>

<h2 id="code-appendix">Code appendix</h2>

<p>Herein lies the code required to produce the figure above.
Caveat emptor.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>library(TESS)

set.seed(42)

lambda &lt;- 1.0
mu &lt;- 0.6
nsim &lt;- 250
nboot &lt;- 100
widths &lt;- seq(0.05, 0.95, 0.05)

fitCR &lt;- function(bt) {
  fn &lt;- function(par) {
    -tess.likelihood(bt, exp(par[1]), exp(par[2]))
  }
  r &lt;- log((length(bt) + 1) / 2) / max(bt)
  opts &lt;- optim(log(c(r + 0.1, 0.1)), fn)
  opts$par &lt;- exp(opts$par)
  return(opts)
}

npBootCR &lt;- function(bt, nboot, widths) {
  rates &lt;- sapply(seq.int(nboot), function(idx) {
    fit &lt;- fitCR(sample(bt, replace = TRUE))
    return(c(b = fit$par[1], d = fit$par[2]))
  })
  half_alphas &lt;- (1 - widths) / 2
  apply(rates, 1, function(samps) {
    sapply(half_alphas, function(alpha_2) {
      quantile(samps, c(alpha_2, 1 - alpha_2))
    }, simplify = FALSE)
  })
}

parBootCR &lt;- function(bt, nboot, widths) {
  opts &lt;- fitCR(bt)
  point &lt;- opts$par

  sims &lt;- tess.sim.taxa(n = nboot, nTaxa = length(bt) + 1, lambda = point[1], mu = point[2], max = 100)
  rates &lt;- sapply(seq.int(nboot), function(idx) {
    fit &lt;- fitCR(ape::branching.times(sims[[idx]]))
    return(c(b = fit$par[1], d = fit$par[2]))
  })
  half_alphas &lt;- (1 - widths) / 2
  apply(rates, 1, function(samps) {
    sapply(half_alphas, function(alpha_2) {
      quantile(samps, c(alpha_2, 1 - alpha_2))
    }, simplify = FALSE)
  })
}

extractCoverage &lt;- function(reps, widths, true_par = c(b = lambda, d = mu)) {
  res &lt;- lapply(c("b", "d"), function(par) {
    covered &lt;- sapply(reps, function(sim) {
      sapply(sim[[par]], function(low_high) {
        return(as.integer(low_high[1] &lt; true_par[par] &amp;&amp; low_high[2] &gt; true_par[par]))
      })
    })
    dimnames(covered) &lt;- list(widths, NULL)
    return(covered)
  })
  names(res) &lt;- c("b", "d")
  return(res)
}

trees &lt;- tess.sim.age(n = nsim, age = 10, lambda = lambda, mu = mu)
times &lt;- sapply(trees, ape::branching.times)

system.time({
  np_cis &lt;- lapply(times, npBootCR, nboot = nboot, widths = widths)
})

system.time({
  par_cis &lt;- lapply(times, parBootCR, nboot = nboot, widths = widths)
})

np_coverage &lt;- extractCoverage(np_cis, widths = widths)
np_birth_coverage &lt;- cbind(widths, rowMeans(np_coverage$b))
np_death_coverage &lt;- cbind(widths, rowMeans(np_coverage$d))


par_coverage &lt;- extractCoverage(par_cis, widths = widths)
par_birth_coverage &lt;- cbind(widths, rowMeans(par_coverage$b))
par_death_coverage &lt;- cbind(widths, rowMeans(par_coverage$d))

plot(np_birth_coverage, col = "#ffa035", pch = 1, xlab = "Nominal quantile", ylab = "Coverage")
points(np_death_coverage, col = "#00a7b0", pch = 1)
points(par_birth_coverage, col = "#ffa035", pch = 4)
points(par_death_coverage, col = "#00a7b0", pch = 4)
abline(a = 0, b = 1, col = "#66666690")
legend(
  "topleft",
  legend = c("Birth rate", "Death rate", "Nonparametric bootstrap", "Parametric bootstrap"),
  col = c("#ffa035", "#00a7b0", "black", "black"),
  pch = c(16, 16, 1, 4),
  border = NA,
  bty = "n"
)
</code></pre></div></div>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>As opposed to a crazy-good idea, which everyone loves. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>There’s nothing wrong in general with figuring out the answer to something just because we don’t know it. But if we’re trying to develop methods to lubricate the wheels of the scientific endeavor, it helps to know that there’s anyone out there asking the question. Or that someone <em>should</em> be asking the question. <a href="https://afmagee.github.io/how-not-to-measure-treespace/">“What is the dimensionality of treespace?”</a> is a question that I have yet to establish has any <em>practical</em> relevance. Even if it’s interesting. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>And we should expect, in general, parameters to be correlated. We know birth-death models are a mess of theoretical and practical identifiability issues. So, we might as well try to get at some of that joint uncertainty. Why not do multivariate likelihood-based confidence intervals? The same reason we use MCMC and not quadrature for Bayesian inference, it’s a nonstarter past a handful of dimensions. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>If we condition on the tip times, then it may well be as simple as sampling, for each tip time, a divergence time at least as old. That is, they may be conditional samples from the same CDF. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>The majority, I’d hazard. Possibly the vast majority. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>If we have good enough sampling, or at least a handle on how good our sampling is, then this could be a way to get at the distribution of the total number of infections in an outbreak with fewer assumptions about the branching process model itself. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[I like a good crazy idea1. I also, clearly, have a problem with answers in search of questions2. In “a little bit of column A, a lot of column B”: bootstrapping branching times in a time-calibrated phylogeny. This is only a little crazy, because in time-varying phylogenetic birth-death models where all taxa are sampled at the present, divergence times are IID.. Why is it an answer in search of a solution? Let’s take a look. As opposed to a crazy-good idea, which everyone loves. &#8617; There’s nothing wrong in general with figuring out the answer to something just because we don’t know it. But if we’re trying to develop methods to lubricate the wheels of the scientific endeavor, it helps to know that there’s anyone out there asking the question. Or that someone should be asking the question. “What is the dimensionality of treespace?” is a question that I have yet to establish has any practical relevance. Even if it’s interesting. &#8617;]]></summary></entry><entry><title type="html">How not to measure the dimensionality of treespace</title><link href="https://afmagee.github.io/how-not-to-measure-treespace/" rel="alternate" type="text/html" title="How not to measure the dimensionality of treespace" /><published>2025-12-05T00:00:00+00:00</published><updated>2025-12-05T00:00:00+00:00</updated><id>https://afmagee.github.io/how-not-to-measure-treespace</id><content type="html" xml:base="https://afmagee.github.io/how-not-to-measure-treespace/"><![CDATA[<p>As you can perhaps tell from the title, this is a post about failure.
Awhile ago, I got what I thought was a clever idea<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.
And I was so sure of myself I jumped straight to making a repo for the code that would assuredly lead to a brilliant manuscript<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>.
I’m <a href="https://github.com/afmagee/phylo_dim">leaving it up</a> as a testament to my failure, but since the problem still bugs me, I’m writing this up.</p>

<h2 id="the-scientific-question">The scientific question</h2>

<p>A question that comes up in phylogenetic contexts a lot is “do these two loci share a phylogeny?”
In particular, I’m talking about cases where we’re interested in underlying differences in topology, and not simply branch lengths<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.
The last time this question came up for me, I was trying to identify recombination breakpoints in some viral genomes.
But this question can also come up if we’re feeling lazy and don’t <em>really</em> want to use species tree methods and hope we can just concatenate things.
Or even if we’ve given up and know we need the multispecies coalescent but want to reduce the number of gene trees involved.</p>

<p>From a statistical perspective, we can frame this as a hypothesis test, or as model selection.
For simplicity’s sake, let’s consider only two loci, so our models are that there is one tree, or that there are two trees.
If we want to do hypothesis testing, we need some (asymptotic) distribution of a test statistic, like in a likelihood ratio test (LRT), where (twice) the difference in maximized log-likelihoods between the models follows (approximately, asymptotically) a chi-squared distribution.
(Or we need to undertake am expensive brute-force simulation-based approach<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>.)
If we want to do model selection, and we don’t want to wait on marginal likelihoods<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup>, we might like to do something like AIC or BIC, to penalize the two-tree model for complexity.
Either way, we need to know how much more complex the two-tree model is.
That is, how many more parameters does it have?
The easy part: it has twice as many branches.
The hard part: it has twice as many trees.</p>

<h2 id="my-not-so-clever-methodology">My not-so-clever methodology</h2>

<p>In brief, my notion to address this question was to swap around input and output in a LRT.
Roughly speaking, a LRT takes in as input the degrees of freedom (the difference in the number of free parameters between the more complex and the nested simpler model), an observed difference in log-likelihoods between two models, and outputs a probabilistic judgement on whether the more complex model is justifiably better.
It does this via the asymptotic approximation that the distribution of the LRT statistic is chi-squared.
What I had in mind was to feed in many observed differences in log-likelihood, a known judgement on whether the complex model is justifiably better (it would not be), and output the degrees of freedom.</p>

<h3 id="what-is-normal-anyways">What is Normal, anyways?</h3>

<p>If that doesn’t make sense<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup>, let’s try an example.
And for the moment, let’s stick with Normal distributions<sup id="fnref:7" role="doc-noteref"><a href="#fn:7" class="footnote" rel="footnote">7</a></sup>.
Imagine that I tell you I will simulate independently $m$ times from the following experiment.
I draw $kn$ observations, where $n$ is large, of a Normal($\mu$, $\sigma^2$) distribution.
Then I fit two models, and give you their likelihoods.
The simpler (null) model is that all $kn$ draws come from the same Normal, whose mean and variance I estimate.
The more complex model is, for some $k$, that there are $k$ sets of $n$ draws, each from their own Normal distribution whose mean and variance I estimate.</p>

<p>You might choose to record, per experiment, twice the difference in log-likelihoods of the two models.
Then, the $m$ values are independent draws from a distribution which is asymptotically Chi-squared, with $2(k - 1)$ degrees of freedom (where the 2 is because you know that each component comes with a mean and a variance to estimate).
This means that, in theory, you can fit $k$ to the $m$ LRT statistics and tell me what it is.</p>

<p>Now, the question, does it work?
The answer would seem to be yes.
When I simulate 1000 times with $k = 6$, the best-fitting Chi-squared distribution has 10 degrees of freedom, which is the right answer.</p>

<p><img src="../assets/images/posts/2025-12-05_norm_dim.png" alt="Histogram of 1000 samples of the LRT statistic with the two Normal models under consideration in grey and k = 6. Best-fitting Chi-squared distribution in black has 10 degrees of freedom." /></p>

<h2 id="the-results-for-trees">The results for trees</h2>

<p>My plan for trees was pretty similar.
For each experiment, at a given tree length and number of tips, I would simulate one true tree.
I would then simulate an alignment with $2n$ sites in it, and fit two models with maximum likelihood, a one-tree model and a two-tree model.
To keep things simple, I’d use Jukes-Cantor so that the difference in free parameter count between the models would be the tree topology and the number of branches.</p>

<p>I decided to do this for 4 through 10 tip trees, because IQ-TREE can infer trees that size nearly instantaneously so 200 replicates at each tree size for a few tree lengths would be feasible before I tried to scale up.</p>

<p>However, when I summarized these simulations, to my surprise I found that the degrees of freedom was simply the number of branches in the tree.
This meant that I was inferring that the number of free parameters a tree was worth was… 0.
Because all the free parameters were going into the branch lengths on the second tree.</p>

<p><img src="../assets/images/posts/2025-12-05_LRT_dim.png" alt="I'm pretty sure that a tree is worth more than 0 free parameters, but the simulations aren't exactly backing me up on that." /></p>

<h2 id="what-went-wrong">What went wrong?</h2>

<p>So, why did this go off the rails?</p>

<p>The LRT is an asymptotic result, and as such I chose to simulate large alignments of 100,000 sites.
For the shortest simulated tree length (0.25) for the largest tree (10 tips, 17 branches) the average branch has 1470.6 substitutions on it.
That’s probably large enough for the asymptotics, but unfortunately it’s also a guarantee that we get the right tree topology every time<sup id="fnref:8" role="doc-noteref"><a href="#fn:8" class="footnote" rel="footnote">8</a></sup>.
In other words, the effect of the tree topology isn’t showing up in our simulations because, in the region we’re simulating, there is no effect of the tree topology.</p>

<p>Really, then, the problem is that I tried to use an asymptotic tool to answer a non-asymptotic question.
The effect of phylogenetic uncertainty lives in a world where we don’t have an effectively infinite amount of data about the topology.
Given how skeptical I often am about asymptotic results, I feel like I should have seen this coming in advance.
Alas.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>This is <em>always</em> a warning sign. Trying to be clever produces bimodal outcomes and the bigger mode is decidedly “well that wasn’t such a good idea, now, was it?” <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>This is why we have sandbox repos where we FA before inevitably FOing. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>This is just a question of continuous model parameters, so it fits in the usual statistical toolkit just fine. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>For example, fit the one- and two-tree models to real data, compute the difference in likelihood. Then, to get the null distribution, simulate a few hundred alignments under the one-tree model. Fit both one- and two-tree models to each, record the difference in likelihood. Easy, but slow. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5" role="doc-endnote">
      <p>Though perhaps this is a good use-case for one of our <a href="https://academic.oup.com/sysbio/article/69/2/209/5555782">19 dubious approaches</a> for fast approximate marginal likelihoods? <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6" role="doc-endnote">
      <p>And it didn’t when I tried to explain it to a much smarter colleague, so perhaps again I should have known I was destined for trouble. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7" role="doc-endnote">
      <p>As always, the statistician’s crutch. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8" role="doc-endnote">
      <p>At least here where we’re not simulating funky non-neutral substitution models. <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[As you can perhaps tell from the title, this is a post about failure. Awhile ago, I got what I thought was a clever idea1. And I was so sure of myself I jumped straight to making a repo for the code that would assuredly lead to a brilliant manuscript2. I’m leaving it up as a testament to my failure, but since the problem still bugs me, I’m writing this up. This is always a warning sign. Trying to be clever produces bimodal outcomes and the bigger mode is decidedly “well that wasn’t such a good idea, now, was it?” &#8617; This is why we have sandbox repos where we FA before inevitably FOing. &#8617;]]></summary></entry><entry><title type="html">On a generalized Bus Factor</title><link href="https://afmagee.github.io/bus-factor/" rel="alternate" type="text/html" title="On a generalized Bus Factor" /><published>2025-11-10T00:00:00+00:00</published><updated>2025-11-10T00:00:00+00:00</updated><id>https://afmagee.github.io/bus-factor</id><content type="html" xml:base="https://afmagee.github.io/bus-factor/"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Many have previously proposed to measure resiliency of projects or organizations via the “Bus Factor.” 
Bus Factors take values in the positive integers $\mathbb{Z}^{+}$.</p>

<p>The Standard Bus Factor (SBF) is, loosely, the number of people who must be removed from their roles befall before a crucial system cannot be maintained (or similar, such as a project needing to be suspended).
The model of resiliency underlying the SBF is of non-exclusive “or.”
That is, the system will work if individual 1 is present <em>or</em> individual 2 is present <em>or</em>… and so forth.
Thus, resiliency increases with increasing SBF.</p>

<p>A less typical Alternative Bus Factor (ABF) definition runs in the opposite direction, indexing the number of individuals of which the removal of any one is fatal.
The model of resiliency underlying the ABF is “and.”
In this model, a system only works if individual 1 is present <em>and</em> individual 2 is present <em>and</em>… and so forth.
In this case, increasingly large values indicate <em>decreasing</em> resiliency, with more crucial links which can be taken out.</p>

<h2 id="new-approaches">New approaches</h2>

<p>It is not necessary, either mathematically or conceptually, to retain both Bus Factors.
Under the ABF model, failure occurs if one out of any number of other individuals is “bussed.” 
This suggests that we might instead measure and-based Bus Factors in terms of fractions.
Thus, $\frac{1}{3}$ would indicate that if one out of any three key people were bussed, failure would occur, and as the fraction decreases, so does stability.</p>

<p>Thus we propose replacing the SBF and ABF with the Unified Bus Factor (UBF), which takes values in $\frac{1}{\mathbb{Z}^{+}} \cup \mathbb{Z}^{+}$.
The UBF increases with increasing stability, and encompasses both the SBF and ABF.
A UBF of 2 indicates that <em>either</em> of two people are sufficient for resiliency, while a value of 1/2 indicates that <em>both</em> are required.
As the SBF is undefined for values less than 1, we further suggest that the UBF can in general be safely used implicitly, requiring only (ill-advised) usage of the ABF to be qualified.</p>

<h2 id="discussion-and-conclusions">Discussion and Conclusions</h2>
<p>The SBF and ABF both presuppose at most one type of individual, the “or” type and the “and” type.
This is not necessarily the case for all systems of interest.
For example, a baseball team requires individuals capable of filling a number of different roles: catcher, pitcher, first base, and so forth.
Without one for every role, the team cannot play, but a team has more than one pitcher available, and thus can play even without one.
This “and”-based unification of multiple “or” types, we leave for future work.</p>

<p>Despite its limitations, the SBF has proven a useful tool for communicating about system resiliency.
We therefore hope that the UBF will provide a helpful extension for enumerating least-stable systems in clear and compact forms.</p>]]></content><author><name>Andrew Magee</name></author><summary type="html"><![CDATA[Introduction Many have previously proposed to measure resiliency of projects or organizations via the “Bus Factor.” Bus Factors take values in the positive integers $\mathbb{Z}^{+}$.]]></summary></entry></feed>