<?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://geo-ant.github.io/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://geo-ant.github.io/blog/" rel="alternate" type="text/html" /><updated>2026-06-02T19:33:20+00:00</updated><id>https://geo-ant.github.io/blog/feed.xml</id><title type="html">Geo’s Notepad</title><subtitle>Mostly Programming and Math</subtitle><entry><title type="html">A Simple Image Brightness And Contrast Adjustment Technique</title><link href="https://geo-ant.github.io/blog/2026/simple-image-contrast-brightness-adjustment/" rel="alternate" type="text/html" title="A Simple Image Brightness And Contrast Adjustment Technique" /><published>2026-05-18T00:00:00+00:00</published><updated>2026-05-18T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2026/simple-image-contrast-brightness-adjustment</id><content type="html" xml:base="https://geo-ant.github.io/blog/2026/simple-image-contrast-brightness-adjustment/"><![CDATA[<p>I recently found myself squinting at two images at work and wondering if one
is actually “better” than the other. The two images were
showing the same object, but they had slightly <em>but noticeably</em> different value
ranges. That made it hard for me to understand which of the differences were
due to display brightness and contrast settings and which were part of the
actual structure of the image. Then I remembered a simple technique that
I’ve been using on and off since my PhD days.</p>

<h1 id="the-problem">The Problem</h1>

<p>So let’s say we have two images \(\boldsymbol{I}\) and \(\boldsymbol{J}\) we
want to normalize to a common brightness range. Despite showing the same
structures, maybe we have generated the two images using slightly different
processing techniques. The structures of interest might present a little differently,
but the value ranges might also be shifted noticeably between the images. Using
the minimum and maximum values to normalize the value ranges is often a bad idea
because those properties can be very sensitive to noise. On the other hand,
using the mean or mode to scale one image to the other is often good enough,
but gives us only one degree of freedom. Note that we assume that the images
are spatially aligned and the only thing we want to correct is a brightness
transform.</p>

<h1 id="a-simple-brightness-transform">A Simple Brightness Transform</h1>

<p>Now let’s find a simple brightness transform that maps image \(\boldsymbol{J}\)
to image \(\boldsymbol{I}\). Let’s allow a linear brightness transform with
coefficients because such a transform is typically so constrained that it won’t
alter the structures of interest. It will only help us shift one image to the
value range of the other image to make it comparable. Every pixel
\(J_k\) at index \(k\) of image \(\boldsymbol{J}\) is transformed as<sup id="fnref:pixel-index" role="doc-noteref"><a href="#fn:pixel-index" class="footnote" rel="footnote">1</a></sup>:</p>

\[J_k \mapsto a J_k + b, \tag{1} \label{transform}\]

<p>where \(a, b \in \mathbb{R}\) are the coefficients of the transformation.
So, what are the <em>best</em> coefficients \(a, b\) such that the brightness
variations between the images are minimized? Well, we can, e.g., minimize
the pixelwise deviations of the images in a least squares sense<sup id="fnref:lsqr" role="doc-noteref"><a href="#fn:lsqr" class="footnote" rel="footnote">2</a></sup>:</p>

\[\min_{a,b} \frac{1}{N} \sum_{k=1}^{N} \left( a J_k +b - I_k \right)^2 \tag{2} \label{ols}\]

<p>The neat thing is that there’s an analytical solution for the coefficients,
which can be found by simply calculating the point at which the partial
derivatives \(\partial/\partial a, \partial/\partial b\) of the expression
both vanish. After some rewriting, this leads us to:</p>

\[\begin{eqnarray}
a &amp;=&amp; \frac{\text{cov}(\boldsymbol{I},\boldsymbol{J})}{\text{var}(\boldsymbol{J})} \label{a-ols} \tag{3a}\\
b &amp;=&amp; \text{mean}(\boldsymbol{I}) - a\cdot \text{mean}(\boldsymbol{J}) \label{b-ols} \tag{3b},
\end{eqnarray}\]

<p>where \(\text{mean}\) calculates the mean value of an image,
\(\text{cov}\) is the <a href="https://en.wikipedia.org/wiki/Covariance">covariance</a>,
and \(\text{var}\) is the <a href="https://en.wikipedia.org/wiki/Variance">variance</a>:</p>

\[\begin{eqnarray}
\text{mean}(\boldsymbol{I}) &amp;=&amp; \frac{1}{N} \sum_{k=1}^{N} I_k \tag{4a} \label{mean}\\
\text{cov}(\boldsymbol{I},\boldsymbol{J}) &amp;=&amp; \frac{1}{N-1} \sum_{k=1}^{N} (I_k-\text{mean}(\boldsymbol{I}))\cdot(J_k- \text{mean}(\boldsymbol{J})) \tag{4b} \label{cov}\\
\text{var}(\boldsymbol{J}) &amp;=&amp; \text{cov}(\boldsymbol{J},\boldsymbol{J}) \tag{4c} \label{var}
\end{eqnarray}\]

<p>That means using eq. \(\eqref{transform}\) with coefficients from \(\eqref{a-ols}\)
and \(\eqref{b-ols}\) to adjust the brightness values in image \(\boldsymbol{J}\)
will adjust the image to fit the brightness range of image \(\boldsymbol{I}\).</p>

<h2 id="weighted-least-squares">Weighted Least Squares</h2>

<p>There’s one more improvement to the technique we can make quite easily, which
is to use <em>weighted least squares</em> as the minimization objective rather than
the <em>ordinary least squares</em> we used above. This allows us to give each
pixel \(k\) a corresponding weight \(w_k\) that modifies its contribution to
the objective function. For example, if our images contain large areas of
near-zero background, we could force areas with higher signal to contribute
more strongly by setting the weights as \(w_k = I_k\); we could also set the
weights for pixels below a threshold to \(0\). There are many possible
ways to improve the optimization with weighting.</p>

<p>For weighted least squares, our minimization objective becomes</p>

\[\min_{a,b} \frac{1}{N} \sum_{k=1}^{N} w_k \left( a J_k +b - I_k \right)^2 \tag{5} \label{wls},\]

<p>and we can find very similar analytical solutions for the coefficients</p>

\[\begin{eqnarray}
a &amp;=&amp; \frac{\text{cov}_{\boldsymbol{w}}(\boldsymbol{I},\boldsymbol{J})}{\text{var}_{\boldsymbol{w}}(\boldsymbol{J})} \label{a-wls} \tag{6a}\\
b &amp;=&amp; \text{mean}_{\boldsymbol{w}}(\boldsymbol{I}) - a\cdot \text{mean}_{\boldsymbol{w}}(\boldsymbol{J}) \label{b-wls} \tag{6b}.
\end{eqnarray}\]

<p>Now we have to use the <em>weighted</em> mean, covariance, and variance defined as</p>

\[\begin{eqnarray}
\text{mean}_{\boldsymbol{w}}(\boldsymbol{I}) &amp;=&amp; \frac{1}{\sum_{k=1}^N w_k} \sum_{k=1}^{N} w_k I_k \tag{7a} \label{mean-w}\\
\text{cov}_{\boldsymbol{w}}(\boldsymbol{I},\boldsymbol{J}) &amp;=&amp; \frac{N}{N-1}\frac{\sum_{k=1}^{N} w_k (I_k-\text{mean}_{\boldsymbol{w}}(\boldsymbol{I}))\cdot(J_k- \text{mean}_{\boldsymbol{w}}(\boldsymbol{J}))}{\sum_{k=1}^N w_k}  \tag{7b} \label{cov-w}\\
\text{var}_{\boldsymbol{w}}(\boldsymbol{J}) &amp;=&amp; \text{cov}_{\boldsymbol{w}}(\boldsymbol{J},\boldsymbol{J}) \tag{7c} \label{var-w}
\end{eqnarray}\]

<p>If those formulas look familiar, it’s because they probably are. What we did is
just bog-standard <a href="https://mathworld.wolfram.com/LeastSquaresFitting.html">linear least squares</a>
fitting of the intensity values.</p>

<h1 id="prior-art-and-further-reading">Prior Art and Further Reading</h1>

<p>Obviously, I wasn’t the first to come up with this least-squares-based linear
brightness normalization technique. In remote sensing it’s known as
<em>radiometric normalization</em> and described, e.g., by <a href="https://www.tandfonline.com/doi/full/10.1080/01431160701271990?scroll=top&amp;needAccess=true">Zhang <em>et al.</em></a>
<sup id="fnref:zhang" role="doc-noteref"><a href="#fn:zhang" class="footnote" rel="footnote">3</a></sup>. The relevant results are given in eqn. (1) and (2) of that paper.
The authors also propose an iterative reweighting that goes beyond the
scope of this article. For a much more sophisticated framework specific to this field, see
<a href="https://www.sciencedirect.com/science/article/abs/pii/S0034425707003495">Canty <em>et al</em>.</a>
<sup id="fnref:canty" role="doc-noteref"><a href="#fn:canty" class="footnote" rel="footnote">4</a></sup></p>

<h1 id="outlook">Outlook</h1>

<p>At the beginning, I said that I wanted to normalize two images to a common
brightness range, but what I’ve actually presented is a technique to transform
one image into the brightness range of another image. How do we know which
image to pick? This might seem pedantic for two images, but the problem becomes
more obvious if we have a set of more than two images that we want to transform
into the same brightness range. We can certainly choose one image as a template
and transform all others into its brightness range, and often that is just fine.
But what if the one image is an outlier and corrupted by noise or artifacts?
Least squares isn’t famous for dealing well with outliers anyway, but in this
case it’s more obvious that the choice of template image can introduce an
unfavorable bias.</p>

<p>Those considerations quickly lead us into the territory of joint optimizations
and <em>latent images</em>, where the math and algorithms get really interesting really
fast, despite our simple model. I might tackle this in a follow-up article.</p>

<h1 id="endnotes">Endnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:pixel-index" role="doc-endnote">
      <p>We can use a linear index for the pixels instead of $x,y$ because the position does not matter for our model. The important thing is that each value of \(k\) corresponds to the same unique pixel position in both images. <a href="#fnref:pixel-index" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:lsqr" role="doc-endnote">
      <p>Least squares fitting has a couple of nice properties. First of all, it emerges as the maximum likelihood estimator of Gaussian probability distributions. Second of all, it is very tractable analytically. Both things make it appealing for our use case, though a formal Bayesian description of our image mapping process can get tricky quickly. So we’ll allow ourselves to use least squares here because it’s a good heuristic. <a href="#fnref:lsqr" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:zhang" role="doc-endnote">
      <p>See <a href="https://www.researchgate.net/publication/234800535_Automatic_relative_radiometric_normalization_using_iteratively_weighted_least_square_regression">here</a> for a PDF copy. <a href="#fnref:zhang" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:canty" role="doc-endnote">
      <p>See <a href="https://www2.imm.dtu.dk/pubdb/pubs/5362-full.html">here</a> for a PDF version. <a href="#fnref:canty" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="image-processing" /><category term="least-squares" /><summary type="html"><![CDATA[I recently found myself squinting at two images at work and wondering if one is actually “better” than the other. The two images were showing the same object, but they had slightly but noticeably different value ranges. That made it hard for me to understand which of the differences were due to display brightness and contrast settings and which were part of the actual structure of the image. Then I remembered a simple technique that I’ve been using on and off since my PhD days.]]></summary></entry><entry><title type="html">A Skateboard Isn’t a Vertical Slice of a Car But It Should Be</title><link href="https://geo-ant.github.io/blog/2026/making-sense-of-mvp/" rel="alternate" type="text/html" title="A Skateboard Isn’t a Vertical Slice of a Car But It Should Be" /><published>2026-04-07T00:00:00+00:00</published><updated>2026-04-07T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2026/making-sense-of-mvp</id><content type="html" xml:base="https://geo-ant.github.io/blog/2026/making-sense-of-mvp/"><![CDATA[<p>It’s been one too many times that I’ve seen the famous <em>skateboard, scooter, bike,
motorcycle, and car</em> <a href="https://blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp">image</a>
by Henrik Kniberg, which he says is about (agile) “product development
in general”. I’ve finally figured out what bothers me about it so much.
I’ll argue that the underlying mental model is at best not helpful
and at worst actively misleading for product development, despite the article
making a couple of excellent points and presenting great examples. Hiding
in Kniberg’s own text and examples is a better mental model: vertical slices.</p>

<p>Let’s remind ourselves of the (in)famous image quickly before we go further.</p>

<figure class="natural">
 <img src="/blog/images/mvp/kniberg-mvp.png" alt="Kniberg's Illustration" style="width:100%" />
   <figcaption> Kniberg's original illustration from <a href="https://blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp">this article</a>. He writes "The picture is a metaphor. It is not about actual car development, it is about product development in general, using a car as a metaphor."</figcaption>
</figure>

<p>This image illustrates the key metaphor presented in the article. Attacking a
metaphor always runs the risk of attacking a strawman by taking the metaphor
too literally. Still, it is fair to examine the metaphor as such to evaluate whether
it’s even a helpful way of illustrating Kniberg’s core thesis. Then, I’ll
also critically evaluate the mental model that underlies
the metaphor. A mental model guides our thinking and consequently our decision-making.
When it is flawed, it can lead us to make bad decisions.</p>

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

<p>At its core, the original article argues that rather than building 
incomplete pieces of a grand vision, start with a simple solution that already 
delivers real user value. Evolve testable, usable, lovable versions through 
feedback. This avoids wasted effort and leads to a better product than executing a 
fixed upfront plan.</p>

<p>Gathering user feedback frequently and using it to iteratively
improve your product during development is great advice. Understanding
actual user needs is hard enough as it is and almost impossible in isolation.
We all know the <em>“If I had asked people what they wanted, they would have said
faster horses”</em> quote often attributed to Henry Ford<sup id="fnref:henry-ford-quote" role="doc-noteref"><a href="#fn:henry-ford-quote" class="footnote" rel="footnote">1</a></sup>
and there’s much wisdom in it. Continuous feedback informing us of how aligned
we are with the user needs is indeed invaluable. I think, as a
framework for <em>product discovery</em>, this is great. It also shines when
used as a mental model for getting us to actually commit to the earliest
testable/usable/lovable/… version of a product. This prevents us from optimizing
something to death in an ivory tower.</p>

<h2 id="digging-deeper">Digging Deeper</h2>

<figure class="natural">
 <img src="/blog/images/mvp/pepe-silvia.jpg" alt="Pepe Silvia Meme" style="width:50%" />
   <figcaption> Me, obsessing over the skateboard metaphor.</figcaption>
</figure>

<p>And yet, something just feels off to me about this skateboard-to-car
metaphor. Of course! No car manufacturer started with a
skateboard, right? Sure, but that’s just taking the metaphor uncharitably
literally. So does the skateboard just mean <em>simpler car</em>? That also feels
wrong. In Kniberg’s article, the skateboard is described to the customer
by saying “don’t worry […] We’re still aiming to build a car, but in the meantime please
try this and give us feedback”. So the metaphorical skateboard <em>is</em> a stepping
stone on the way to a car.</p>

<p>In the article, the first thing that the customer would get delivered
in the <em>Not Like This</em> section is a single wheel of a car, which is of no use
to them. In the <em>Like This</em> section, a skateboard is shipped to them. They are potentially
not happy about it, but it’s definitely something that they can use and
provide feedback on. So <em>usable</em> is one important property of a metaphorical skateboard.
Obviously, it’s also <em>simpler</em> than a (metaphorical) car. And thirdly, it’s
<em>self-contained</em>. It’s not a <em>partial</em> car, in fact: it’s not even <em>part of a car</em>!
You <em>could</em> give a partial car to users to test it, and as a matter of fact that’s
pretty much what I’ll argue later, but still Kniberg chose to go with a skateboard.
Those notions of self-containedness (is that a word?) and independence
are where, I believe, things start to unravel.</p>

<p>Let’s dig into the examples in his text and see if we can understand what
Kniberg could mean by skateboard…</p>

<h3 id="the-examples">The Examples</h3>

<p>I’ll try to keep this short, because this is shaping up to be a long article, but we do
need to talk about the examples in the original article briefly. For the Spotify
example, Kniberg writes:</p>

<blockquote>
  <p>“developers basically sat down and hacked up a technical prototype, put in
whatever ripped music they had on their laptops, and started experimenting
wildly to find ways to make playback fast and stable”</p>
</blockquote>

<p>He goes on to explain that they focused on the singular metric of latency to
make the product viable and used friends and family to test it. I can definitely
see that this is pretty far from what Spotify is today (or even at the time the
article was written), but why not just leave it at prototype? Is a skateboard
a prototype for a car? Hardly. I’d argue the same is true for the Minecraft
example presented in the article <sup id="fnref:minecraft" role="doc-noteref"><a href="#fn:minecraft" class="footnote" rel="footnote">2</a></sup>.</p>

<p>The Big Government example in the original article is a great case study of 
how they released a very narrowly scoped initial version (with respect to both 
capabilities and distribution) and built on the gathered feedback. However, I 
don’t see how that first version is a skateboard except for it being a much 
simpler, stripped-down version compared to the releases that came after. But again,
a skateboard is not a simpler car, it’s not a partial car, it’s not part of a car.</p>

<p>All this is to say that the examples in the text are excellent examples of good
development practices that led to great products, but none of this has anything
to do with the skateboard-to-car mental model and all to do with vertical integration.</p>

<h2 id="a-skateboard-isnt-a-simpler-car">A Skateboard Isn’t a Simpler Car…</h2>

<p>it’s a simpler <em>mode of transportation!</em> I hear you shout at your screen, shaking your copy of
the Agile Manifesto at it. But let’s think about it. The Spotify “skateboard” was 
built around specific commitments in the realm of low-latency music streaming.
Later increments were built on top of those commitments, not independently from
them. The other examples play out much the same way.</p>

<p>One principle that I strongly believe is that successful development needs clear
constraints. Just think of practical reasons like hiring decisions, funding rounds,
technology choices and the like<sup id="fnref:mode-of-transport" role="doc-noteref"><a href="#fn:mode-of-transport" class="footnote" rel="footnote">3</a></sup>. A concrete vision helps by
constraining the solution spaces you’ll explore. And exploration doesn’t come for free.
And yes, do get user feedback early and often, because it will help you align
your vision with user needs.</p>

<p>So sure, in the most charitable interpretation, the skateboard just means to start
simple and get early user feedback. None of this is objectionable, but I’d argue
it’s also not very helpful for planning actual development<sup id="fnref:big-bang" role="doc-noteref"><a href="#fn:big-bang" class="footnote" rel="footnote">4</a></sup>. Kniberg
writes “Think big, but deliver in small functionally viable increments”. I agree
wholeheartedly, but the skateboard metaphor is a very bad way of illustrating
<em>how</em> to do that. One big problem with that article is that it tells us to
deliver functionally viable increments, without explaining how to 
go from one step to the next systematically, other than getting user feedback. 
As a pure product discovery framework, this might seem useful, but there is no
discovery without development.</p>

<p>Say you’ve identified your (metaphorical) skateboard, which,
although less complex than a (metaphorical) car, will likely be a product of decent
complexity. It’ll take requirements analysis, architecture decisions, actual engineering
work, and much more. This is where a lot of the complexity lies. More than that,
the skateboard-to-car metaphor can imply a harmful level of independence of earlier
and later versions. In reality, architectural decisions will compound and components
will be shared across iterations. In that sense, the metaphor doesn’t just fail
as a guide for development: at worst, it actually points in the wrong direction.</p>

<p>Kniberg does acknowledge a <em>continuity of vision</em>, since even at the skateboard
state we’re “still aiming to build a car”, but he doesn’t explicitly acknowledge
the <em>continuity of engineering</em> in his prose<sup id="fnref:morph" role="doc-noteref"><a href="#fn:morph" class="footnote" rel="footnote">5</a></sup>. That’s how the
skateboard-to-car metaphor obscures crucial aspects of the development process
necessary to even get from one stage to the next.</p>

<h2 id="a-better-development-model-vertical-slices">A Better Development Model: Vertical Slices</h2>

<p>One last time, let’s revisit Kniberg’s examples. What do they
actually represent? They are <em>thin vertical
slices</em>, which are a “narrow but complete sliver of the final product vision”<sup id="fnref:vertical" role="doc-noteref"><a href="#fn:vertical" class="footnote" rel="footnote">6</a></sup>.
They are all highly incomplete and/or narrowly scoped when compared
against their later versions, but all examples show a high level of
<em>vertical integration</em>, meaning a lot of system components from top to bottom
have to work together. Let’s check how a vertical slice compares to the
key properties of the (metaphorical) skateboard we identified above.</p>

<p>Are vertical slices usable? Yes, they are, though I much prefer <em>testable</em>,
as Kniberg does, too. That’s actually one of their major benefits. A vertical
slice through a complex system means that many modules will have to work
together successfully. Thus, you really get to pressure test those interfaces,
which is where a lot of the breaking points of a system are. Sure, it’s easy
for a team to deliver a brilliantly polished module, but that’s worthless
without seeing if their assumptions and the formal interface specifications
(you <em>do</em> have those, right?) hold up in reality. Fail early here and iterate,
to save yourself a lot of costly rework later.</p>

<p>Are vertical slices <em>simpler</em> than what comes after? Of course! Are they
<em>self-contained</em>? Yes, that’s a byproduct of pursuing vertical
integration, but they aren’t <em>independent</em> from later versions, nor should they
be. They enable us to get early feedback, but they definitely aren’t what a skateboard is to a car,
and we should all free ourselves from the idea that incremental versions have a high
degree of independence. Your versions, <em>of course</em>,
won’t be independent from each other, <em>that’s the whole point</em>! You’re building
something incrementally. Thinking of vertical slices helps us remember that 
we are iterating towards a (possibly evolving) vision incrementally rather
than building solutions of increasing complexity independently from each other.
Vertical slices make it clear that the decisions you make in
the early stages will have an impact on the later stages. Vertical slices
aren’t a new idea, and spelled out like this, all of this may sound banal, and yet
the skateboard-to-car metaphor exists. This article isn’t about inventing a
new development practice. I’m pointing out what Kniberg’s examples already show,
and arguing that the popular skateboard-to-car metaphor obscures a better
mental model.</p>

<p>And now for the final question: does this mental model help us plan development?
Absolutely. If we prioritize verticals, then this helps us slice our work
into manageable chunks. Prioritize narrow goals which require high vertical
integration and work towards them. Remember Kniberg telling us to “Think big,
but deliver in small functionally viable increments”. Vertical slices help you
achieve exactly that. They force end-to-end integration and thus requirements
have to be concrete enough to implement, interfaces have to be specified <em>and actually used</em>.
Foundational architectural decisions can’t be deferred, but they also don’t have to be made
all at once. This allows you to test internally, both manually and automatically,
ideally through all levels of integration. You’ll catch wrong assumptions while it’s still cheap to correct them, and can ultimately deliver a product early.</p>

<h2 id="conclusion">Conclusion</h2>

<p>The examples in Kniberg’s original article are all excellent illustrations
of good development practices. What made them work wasn’t the skateboard,
it was the fact that the early releases were all thin, integrated vertical
slices. Gathering early user feedback was used to inform future iterations.
Those were incrementally built on the engineering decisions in their predecessors.
The skateboard metaphor obscures that part. If you think “skateboard”,
it’s easy to forget that earlier versions lay the groundwork for their
successive increments.</p>

<p>Think “vertical slices” instead. They are simpler, testable, and most importantly:
you’ll be forced to make decisions that lay the groundwork for subsequent
increments. To my mind, that’s a better mental model of a good incremental
development process, and one that’s both well-known and worth repeating.</p>

<h2 id="endnotes">Endnotes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:henry-ford-quote" role="doc-endnote">
      <p>While it <em>is true</em> that the quote is often attributed to Ford, there’s <a href="https://www.snopes.com/news/2025/02/23/horses-quote-henry-ford/">no evidence</a> of him actually saying it. But <em>“never let the truth get in the way of a good story”</em> –Mark Twain, or was it…? <a href="#fnref:henry-ford-quote" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:minecraft" role="doc-endnote">
      <p>The skateboard of Minecraft is described as: “You couldn’t do much in the first version – it was basically an ugly blocky 3d-landscape where you can dig up blocks and place them elsewhere to build crude structures.”. Surely a simpler version, but does this version of Minecraft (as described) have the same relation to modern Minecraft as a skateboard has to a car? Why not leave it at prototype here as well? If the core point is that this version was playable and testable, I still think that the skateboard isn’t a good metaphor. <a href="#fnref:minecraft" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:mode-of-transport" role="doc-endnote">
      <p>Cut to all AI and DeFi companies with hundreds of millions in funding and absolutely no concrete vision other than to “revolutionize” finance, intelligence - you name it… <a href="#fnref:mode-of-transport" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:big-bang" role="doc-endnote">
      <p>I’ll extend an olive branch here and say that there truly are people who try to design the perfect product in their ivory tower, and yes, those people will most likely never release <em>anything</em>, let alone anything that anyone actually wants. <a href="#fnref:big-bang" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:morph" role="doc-endnote">
      <p>He writes ‘This model is especially suitable for software, since software is, well, Soft. You can “morph” the product as you go’. The words “soft” and “morph” do a lot of heavy lifting in those sentences and I’d say this is a very handwavy description of the development process at best. At best this tells us <em>that</em> software can be changed, not <em>how</em> to navigate the engineering decisions involved in doing so. Again, this totally obscures the fact that engineering decisions in early versions lay the foundations for later increments. <a href="#fnref:morph" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:vertical" role="doc-endnote">
      <p>Quote from Richin Jose: “How not to build an MVP: The Flawed ‘Skateboard to Car’ Analogy” <a href="https://richinjose.medium.com/how-not-to-build-an-mvp-the-flawed-skateboard-to-car-analogy-4920a845f151">here</a>. The article goes on to compare the MVP to a thin slice of layered cake, rather than delivering the whole top layer of frosting. Very tasty mental image, but doesn’t work one bit as a development metaphor, since we’d have to bake the whole cake to deliver a slice… <a href="#fnref:vertical" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="agile" /><category term="development" /><category term="systems-engineering" /><summary type="html"><![CDATA[It’s been one too many times that I’ve seen the famous skateboard, scooter, bike, motorcycle, and car image by Henrik Kniberg, which he says is about (agile) “product development in general”. I’ve finally figured out what bothers me about it so much. I’ll argue that the underlying mental model is at best not helpful and at worst actively misleading for product development, despite the article making a couple of excellent points and presenting great examples. Hiding in Kniberg’s own text and examples is a better mental model: vertical slices.]]></summary></entry><entry><title type="html">Sample Size Calculations for Binomial Distributions</title><link href="https://geo-ant.github.io/blog/2025/binomial-sample-size-calculations/" rel="alternate" type="text/html" title="Sample Size Calculations for Binomial Distributions" /><published>2025-11-18T00:00:00+00:00</published><updated>2025-11-18T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2025/binomial-sample-size-calculations</id><content type="html" xml:base="https://geo-ant.github.io/blog/2025/binomial-sample-size-calculations/"><![CDATA[<p>Say we have a sequence of independent Bernoulli
experiments and we want to make claims about the true probability of success,
given our observations. How many trials do we need to make claims about the true
probability with a certain confidence? Let’s derive this from first principles.</p>

<h2 id="0-overview-caveats-and-goal">0. Overview, Caveats, and Goal</h2>

<p>Stated slightly more formally, the question we want to answer in this
article is: what’s the minimum number of samples for a series of Bernoulli
experiments so we can be confident that the true probability of success is higher
than a certain lower value, given a certain observed fraction of successes?</p>

<p>Please note that this is a well-known subject and I’ll focus on a
particular <em>frequentist</em> approach here that made sense to me. Specifically,
it’s based on the <a href="https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Clopper%E2%80%93Pearson_interval">Clopper-Pearson interval</a>,
which we’ll derive from scratch and use to answer the question above. Wikipedia provides
a <a href="https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval">nice overview</a>
of different confidence intervals for the Binomial distribution and I also
recommend <a href="https://projecteuclid.org/journals/statistical-science/volume-16/issue-2/Interval-Estimation-for-a-Binomial-Proportion/10.1214/ss/1009213286.full">Brown <em>et al.</em></a>
and <a href="https://projecteuclid.org/journals/electronic-journal-of-statistics/volume-8/issue-1/The-cost-of-using-exact-confidence-intervals-for-a-binomial/10.1214/14-EJS909.pdf">Thulin <em>et al.</em></a>
hereafter (Bro01) and (Thu14), respectively. Please also note that Brown <em>et al.</em>
describe the Clopper-Pearson interval as “wastefully conservative” and recommend
other intervals. I will still use it for the following reasons:</p>

<ol>
  <li>Due to its conservative nature, it will lead us to overestimate the number
of samples rather than underestimate them. This is a safe default, for example
if you work in a regulated industry, as I do<sup id="fnref:sample-overest" role="doc-noteref"><a href="#fn:sample-overest" class="footnote" rel="footnote">1</a></sup>.</li>
  <li>Frequentist statistics usually don’t come naturally for me, but this one
made sense to me. I admit, this is totally a me problem, but if
we want to defend your sample size calculations, they <em>should</em> make
sense to us.</li>
  <li>The math involved in this derivation is a great jumping-off point for
a Bayesian perspective, which I might share in a later post.</li>
</ol>

<h2 id="1-deriving-the-clopper-pearson-lower-and-upper-bounds">1. Deriving The Clopper Pearson Lower and Upper Bounds</h2>

<p>A Bernoulli process is a series of independent Bernoulli experiments with
outcome <em>success</em> with probability \(p\) and <em>failure</em> with probability
\(1-p\). Let \(x\) denote the number of successes,
then the probability of observing exactly \(k \in \mathbb{N}\) successes, given
the total number \(n\) of experiments and the probability of success \(p\) is</p>

\[P(x = k \mid n,p) = \binom{n}{k}p^k (1-p)^{n-k}, \label{binomial-pdf} \tag{1}\]

<p>where necessarily \(k \leq n\). This is the <a href="https://en.wikipedia.org/wiki/Binomial_distribution">Binomial distribution</a>.
The probability of observing <em>at least</em> \(k\) successes is</p>

\[P(x \geq k \mid n,p) = \sum_{k'=k}^n P(x = k' \mid n,p). \label{binomial-cdf} \tag{2}\]

<p>Now, given an observation of \(x \geq k\) in \(n\) samples, the Clopper-Pearson
bound for confidence level \(\gamma = 1-\alpha \in (0,1)\) is given as
the interval \((p_L,p_U)\), where</p>

\[P(x \geq k \mid n,p_L) = \frac{\alpha}{2}, \tag{3} \label{clopper-pl}\]

<p>where \(p_L\) can be understood as the smallest probability of success \(p = p_L\),
for which observing \(x\geq k\) successes can happen “by pure chance”
with probability \(\geq \alpha/2\). The upper bound is</p>

\[P(x \leq k \mid n,p_U) = \frac{\alpha}{2}, \tag{4} \label{clopper-pu}\]

<p>where \(p_U\) can be understood as the largest probability of success \(p = p_U\),
for which observing \(x \leq k\) successes can happen with probability \(\geq \alpha /2\).
This offers a reasonably intuitive way of bounding the true probability,
given \(k\) or more observed successes<sup id="fnref:conservative" role="doc-noteref"><a href="#fn:conservative" class="footnote" rel="footnote">2</a></sup>. However, when stated
like this, eqns. \((\ref{clopper-pl})\) and \((\ref{clopper-pu})\) aren’t very helpful in
finding those bounds. Let’s derive useful expressions for the lower and upper
bounds. But first, we’ll have to take a detour into beta functions and the beta
distribution.</p>

<h3 id="11-prerequisites-the-beta-distribution-and-beta-functions">1.1 Prerequisites: The Beta Distribution and Beta Functions</h3>

<p>Trust me, this detour will make sense in a moment. Say we have a variable \(y \in [0,1]\)
that follows a <a href="https://en.wikipedia.org/wiki/Beta_distribution">beta distribution</a>,
then its probability density (PDF) is given as follows:</p>

\[\begin{eqnarray}
Y &amp;\sim&amp; Beta(a,b) \\
\Rightarrow P(y \mid a,b) &amp;=&amp; \beta^{pdf}_{a,b}(y) = \frac{y^{a-1} (1-y)^{b-1}}{B(a,b)}, \tag{5}\label{beta-pdf}
\end{eqnarray}\]

<p>where \(a,b \in \mathbb{R}\) with \(a,b&gt; 0\). \(B(a,b)\) denotes
the <a href="https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function">beta function</a>.
Note that we have to differentiate between the beta <em>function</em> and the beta <em>distribution</em>.
I’ve denoted the probability density function with \(\beta^{pdf}_{a,b}(y)\), which
is nonstandard notation. This is just to convey that this is a function of
\(y\) parametrized by \(a,b\). It’s cumulative distribution function (CDF) is given
as</p>

\[P(y \leq y_0 \mid a,b) = \beta^{cdf}_{a,b}(y_0) = I_{y_0}(a,b), \tag{6} \label{beta-cdf}\]

<p>where \(I_y(a,b)\) is the <a href="https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function"><em>regularized</em> incomplete beta function</a>,
defined as</p>

\[I_y(a,b) = \frac{B(y;a,b)}{B(a,b)}, \tag{7} \label{ribeta}\]

<p>and \(B(x;a,b)\) is the <a href="https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function">incomplete beta function</a>.
Note again, that I’ve introduced a nonstandard symbol \(\beta^{cdf}_{a,b}\) for the
cumulative distribution function (note the “cdf” superscript rather than “pdf”) now.
Again, this is just a way to express the CDF as a function of \(y\) with parameters
\(a,b\). Finally, we also introduce the <a href="https://en.wikipedia.org/wiki/Quantile_function"><em>quantile function</em></a> (QF),
also called <em>percent-point function</em> (PPF), of the Beta-distribution. It’s (as always) defined
as the inverse of the cumulative distribution function:</p>

\[\beta^{qf}_{a,b}(p) = \beta^{cdf,-1}_{a,b}(p). \tag{8} \label{beta-qf}\]

<p>First note that \(\beta^{qf}_{a,b}\) is the <em>inverse function</em> not the multiplicative
inverse of \(\beta^{cdf}_{a,b}\), such that \(\beta^{cdf}_{a,b}(\beta^{qf}_{a,b}(p))=p\).
On Wikipedia, (Bro01), and (Thu14) the symbol \(B(y;a,b)\) is also used for the
quantile function of the beta distribution, which will get confusing very
quickly. I will only ever use \(B(y;a,b)\) to denote the <em>incomplete</em> beta
function in the following calculations. The quantile function of the beta
distribution does not have a closed form expression (that I know of), but it
can be calculated numerically. For example, in Python it can be calculated via
<a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.beta.html#scipy.stats.beta"><code class="language-plaintext highlighter-rouge">scipy.stats.beta.ppf</code></a>.</p>

<h3 id="12-the-upper-clopper-pearson-bound">1.2 The Upper Clopper Pearson Bound</h3>

<p>Let’s first rewrite eq. \((\ref{clopper-pu})\), because the
left hand side is the cumulative distribution, which <a href="https://en.wikipedia.org/wiki/Binomial_distribution#Cumulative_distribution_function">we know</a>
can be written like this:</p>

\[\begin{eqnarray}
P(x \leq k \mid n,p) &amp;=&amp; I_{1-p}(n-k,k+1) \\
&amp;=&amp; 1-I_{p}(k+1,n-k) \\
&amp;=&amp; 1-\beta_{k+1,n-k}^{cdf}(p), \tag{9} \label{bi-cum}
\end{eqnarray}\]

<p>where the second line is a <a href="https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function">well-known property</a>
of the regularized incomplete beta function. Plugging this into \((\ref{clopper-pu})\)
leads us to a closed form solution \(p_U\) using the quantile function of the beta-distribution
with \(a = k+1\), \(b = n-k\):</p>

\[\boxed{p_U = \beta^{qf}_{k+1,n-k}\left(1-\frac{\alpha}{2}\right)} \tag{10} \label{pu}\]

<h3 id="13-the-lower-clopper-pearson-bound">1.3 The Lower Clopper-Pearson Bound</h3>

<p>It turns out that the left hand side of eq. \((\ref{clopper-pl})\) can also
be written in terms of the CDF of the beta distribution with \(a = k\)
and \(b = n-k+1\) (Bro01, Thu14):</p>

\[P(x \geq k \mid p,n) = \beta^{cdf}_{k,n-k+1}(p) \label{johnson-equality} \tag{11}\]

<p>for \(k&gt;0\). I’ll give a proof for this identity in the next section. Using it
with eq. \((\ref{clopper-pl})\) allows us to write the lower bound as</p>

\[\boxed{p_L =  \beta^{qf}_{k,n-k+1}\left(\frac{\alpha}{2}\right)} \tag{12} \label{pl-2}\]

<h3 id="131-interlude-a-proof">1.3.1 Interlude: A Proof</h3>

<p>We’ll quickly walk through a proof of eq. \((\ref{johnson-equality})\) here, feel
free to skip this section. Plugging \((\ref{beta-cdf})\) into \((\ref{johnson-equality})\),
the claim we want to prove is:</p>

\[P(x \geq k \mid p,n) \overset{?}{=} I_p(k,n-k+1)\]

<p>We can rewrite the left hand side and use eqns. \((\ref{bi-cum})\) and
\((\ref{beta-cdf})\) so that:</p>

\[\begin{eqnarray}
P(x \geq k \mid p,n) &amp;=&amp; 1-P(x &lt; k \mid p,n) \\
&amp;=&amp; 1- (P(x \leq k \mid p,n) - P(x = k \mid p,n)) \\
&amp;=&amp; I_p(k+1,n-k) + P(x = k \mid p,n).
\end{eqnarray}\]

<p>This implies that we have to prove</p>

\[I_p(k+1,n-k) + P(x = k \mid p,n) \overset{?}{=} I_p(k,n-k+1).\]

<p>We now use <a href="https://en.wikipedia.org/wiki/Beta_function#Properties_2">the following recurrence relations</a>
for the regularized incomplete beta function:</p>

\[\begin{eqnarray}
I_y(a+1,b) &amp;=&amp; I_y(a,b) - \frac{y^a (1-y)^b}{a B(a,b)}\\
I_y(a,b+1) &amp;=&amp; I_y(a,b) + \frac{y^a (1-y)^b}{b B(a,b)}.
\end{eqnarray}\]

<p>Plugging this into the equation to prove above and rearranging a bit
gives us:</p>

\[P(x = k \mid p,n) \overset{?}{=} \frac{1}{B(k,n-k)}\frac{n}{k(n-k)} p^k (1-p)^{n-k}\]

<p>Now we have to use the fact that the beta-function can be <a href="https://en.wikipedia.org/wiki/Beta_function#Relationship_to_the_gamma_function">written in terms
of the \(\Gamma\)-function</a>
as well as the fact that \(\Gamma(m) = (m-1)!\) for nonzero integers \(m\).
Using this, gives us:</p>

\[\begin{eqnarray}
P(x = k \mid p,n) &amp;\overset{?}{=}&amp; \frac{(n-1)!}{(k-1)!\cdot (n-k-1)!}\frac{n}{k(n-k)} p^k (1-p)^{n-k} \\
 &amp;=&amp; \binom{n}{k} p^k (1-p)^{n-k},
\end{eqnarray}\]

<p>where the last line is obviously true, because \(P(x = k \mid p,n)\) is the
probability density function of the binomial distribution as written in \((\ref{binomial-pdf})\).
That proves the desired equality.</p>

<h2 id="14-clopper-pearson-bounds-summary">1.4 Clopper-Pearson Bounds: Summary</h2>

<p>In summary, the calculations give us <em>two sided</em> a \(\gamma\)-confidence interval
for the true probability of success \(p\) in a series of \(n\) Bernoulli experiments
given an observation of \(k\) <em>or more</em> successes. That interval is:</p>

\[\boxed{p_L &lt; p &lt; p_U}, \tag{13}\label{two-sided}\]

<p>with \(p_L\) and \(p_U\) calculated as in eqns. \((\ref{pl-2})\) and \((\ref{pu})\),
respectively.</p>

<h2 id="2-a-one-sided-clopper-pearson-style-bound">2 A One-Sided Clopper-Pearson Style Bound</h2>

<p>Often, we don’t really care about an upper bound for the probability and we
are only interested in a lower limit, given \(k\) <em>or more</em> successes in
a series of \(n\) experiments. To get to the \(\gamma = 1-\alpha\) one sided
confidence interval \((p_l,1]\), where \(p_l\) is the lower bound of the
probability, we can proceed analogously to the steps above and require:</p>

\[P(x \geq k \mid p_l,n) = \alpha. \tag{14}\]

<p>Note the <em>lower case</em> \(l\) in \(p_l\) vs the upper case \(L\) in
the lower end of the two sided bound \(p_L\). Using the same logic as before,
this allows us to write the lower bound as</p>

\[\boxed{p_l =  \beta^{qf}_{k,n-k+1}\left(\alpha\right)} \tag{15} \label{pl-1}\]

<p>for \(k&gt;0\). Going forward, I’ll use this bound for sample size calculations, since I consider
the question “how confident can I be that my success probability is <em>at least</em>
\(p_l\), given \(k\) or more observed successes” more relevant for finding a
<em>minimum sample size</em> than a two-sided bound. We can always find the two-sided
bound \((p_L,p_U)\) after the fact, when we’ve performed the actual experiments.</p>

<h2 id="3-sample-size-estimation">3 Sample Size Estimation</h2>

<p>Say we want to prove that the true probability of success \(p\) is larger
than \(90\%\). How many samples do we need? Well, that depends on another
design aspect of our experiment, which is the ratio \(r \in (0,1]\) of successes
that  we expect to observe in a successful run. It should make sense intuitively
that we should need fewer samples if we want to claim \(p&gt;90\%\) with \(95\%\) confidence, when
we have designed our experiment such that we require a ratio of \(r = 99\%\)
or more observed successes, versus if we require only \(90\%\) or more observed successes.
For a given \(n\), the ratio \(r\) means we observe \(k = \lfloor r\cdot n \rfloor\)
or more successes. We’ll typically chose \(r\) to be larger or equal to the lower bound
for \(p\) that we want to claim<sup id="fnref:success-rate" role="doc-noteref"><a href="#fn:success-rate" class="footnote" rel="footnote">3</a></sup>. However, we also have to be careful
not to choose \(r\) so high that we fail because we set the bar too high.
So, in a way, \(r\) also measures how much we think that \(p_l\) underestimates
the true \(p\). This might venture dangerously close to a Bayesian way of thinking,
so I’ll stop this line of thought for now.</p>

<p>To claim that the true \(p\) is larger than a certain lower limit \(p_{min}\)
with confidence \(\gamma = 1-\alpha\), we can require that the corresponding
one-sided Clopper-Pearson bound \(p_l\) becomes at least as large as \(p_{min}\)
for the given confidence level \(\gamma\), sample size \(n\), and ratio of observed successes
\(r\). Since \(\gamma\), \(p_{min}\), and \(r\) are fixed, we can treat \(p_l\)
as a function of \(n\). We’re now looking for the smallest \(n_0\), such that
\(p_l \geq p_{min}\), for all \(n\geq n_0\), formally</p>

\[\begin{eqnarray}
  n_0 &amp;=&amp; \min \left\{n': p_l(n,r,\alpha) \geq p_{min} \; \forall n\geq n' \right\}.\\
  p_l(n,r,\alpha) &amp;:=&amp; \beta^{qf}_{\lfloor r\cdot n\rfloor,n-\lfloor r\cdot n\rfloor+1}(\alpha) 
\end{eqnarray}\]

<p>It’s pretty straightforward to find \(n_0\) with a brute force search, so
I won’t go deep into implementation details, but there is one important caveat
worth mentioning. For completeness, here’s Python code<sup id="fnref:wiki-clopper" role="doc-noteref"><a href="#fn:wiki-clopper" class="footnote" rel="footnote">4</a></sup> for calculating
\(p_l(n)\) given \(r\), \(\alpha\):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">from</span> <span class="nn">scipy.stats</span> <span class="kn">import</span> <span class="n">beta</span>

<span class="k">def</span> <span class="nf">p_lower</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">alpha</span><span class="p">):</span>
   <span class="n">k</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">floor</span><span class="p">(</span><span class="n">r</span><span class="o">*</span><span class="n">n</span><span class="p">)</span>
   <span class="n">temp</span> <span class="o">=</span> <span class="n">beta</span><span class="p">.</span><span class="n">ppf</span><span class="p">(</span><span class="n">alpha</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">n</span><span class="o">-</span><span class="n">k</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
   <span class="k">if</span> <span class="n">np</span><span class="p">.</span><span class="n">isnan</span><span class="p">(</span><span class="n">temp</span><span class="p">):</span>
      <span class="k">return</span> <span class="mf">0.0</span>
   <span class="k">else</span>
      <span class="k">return</span> <span class="n">temp</span>
</code></pre></div></div>

<p>The first idea might be to just find the smallest \(n_0\) for
which \(p_l(n_0)\geq p_{min}\), which is <em>almost</em> correct but there’s an
important caveat: the innocuous “for all \(n \geq n_0\)” qualifier above.
Let’s illustrate this by plotting \(p_l(n)\) for an example,
which will make the problem immediately obvious.</p>

<figure>
 <img src="/blog/images/clopper-pearson/clopper-bound.png" alt="Lower Clopper Pearson Bounds as a Function of Sample Size" style="width:100%" />
 <figcaption>
  <b>Figure 1</b>. The lower one-sided Clopper-Pearson bound as
  a function of sample size for a fixed ratio of observed successes.
  Parameters are &gamma;=90%, r = 85%, p<sub>min</sub> = 80%.
</figcaption>
</figure>

<p>We can see that the \(p_l(n)\) curve has a jagged look, which is <em>not an artifact</em>,
but a consequence of having to round the number of successes \(k\) for a fixed success
ratio \(r\) to an integer number. That means we can’t just take<sup id="fnref:min-number" role="doc-noteref"><a href="#fn:min-number" class="footnote" rel="footnote">5</a></sup> the first \(n_0\) for
which \(p_l(n)\) crosses the desired threshold of \(p_{min}\). Due to the jagged
nature, there might be larger numbers \(n&gt;n_0\) in the vicinity for which the
probability drops below the threshold.</p>

<p>Thus, what we have to do is calculate
\(p_l(n)\) in a range \([n_{min},n_{max}]\) and make sure that we take the smallest
\(n_0\) such that the condition \(p_l(n)&gt;p_{min}\) is satisfied for all
\(n&gt;n_0\) inside that range. This shouldn’t be a problem in practice because
we can choose \(n_{max}\) large enough that we can make the claim for all 
possible sample sizes in practice.</p>

<h2 id="references">References</h2>

<p>(Bro01) Lawrence D. Brown, T. Tony Cai, Anirban DasGupta. “Interval Estimation for a Binomial Proportion.”
Statistical Science, 16(2) 101-133 May 2001. <a href="https://doi.org/10.1214/ss/1009213286">link</a></p>

<p>(Thu14) Måns Thulin. “The cost of using exact confidence intervals for a binomial proportion.” Electronic Journal of
Statistics, 8(1) 817-840 2014. <a href="https://doi.org/10.1214/14-EJS909">link</a></p>

<h2 id="endnotes">Endnotes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:sample-overest" role="doc-endnote">
      <p>This might or might not be a problem in practice. If we are dealing with <em>in silico</em> simulations, then increasing the number of samples might be reasonably cheap. If we have to e.g. recruit patients for a study, this can become a problem. <a href="#fnref:sample-overest" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:conservative" role="doc-endnote">
      <p>Again, do keep in mind that Brown <em>et al.</em> characterize the bounds as “wastefully conservative”. <a href="#fnref:conservative" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:success-rate" role="doc-endnote">
      <p>You can also choose \(r = p_l\), but this will likely blow up your sample size a lot. You can also choose \(r&lt;p_l\), but that will not allow you to reach high confidence levels of \(p&gt;p_l\). That should make sense intuitively. <a href="#fnref:success-rate" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:wiki-clopper" role="doc-endnote">
      <p>Cf. also <a href="https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Clopper%E2%80%93Pearson_interval">the Python code on Wikipedia</a>, where that is for computing the <em>two-sided</em> interval, and hence uses \(\alpha/2\). <a href="#fnref:wiki-clopper" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:min-number" role="doc-endnote">
      <p>We <em>can</em> do that if we can guarantee that all our experiments will have exactly this number of tries, but then we cannot treat this \(n_0\) as the <em>minimum</em> number of samples to make our claim. If one experiment has more tries than this \(n_0\), then it might land in one of the dips, which might be a problem. And don’t even think about throwing away some of the measured data to reduce the number of samples, since then the question becomes: “which data do you throw away?” and there will <em>rarely if ever</em> be a good answer to that question that doesn’t look like we doctored our statistics. <a href="#fnref:min-number" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="statistics" /><category term="math" /><summary type="html"><![CDATA[Say we have a sequence of independent Bernoulli experiments and we want to make claims about the true probability of success, given our observations. How many trials do we need to make claims about the true probability with a certain confidence? Let’s derive this from first principles.]]></summary></entry><entry><title type="html">A Tale of Testability and Sending Non-Send Types in Rust</title><link href="https://geo-ant.github.io/blog/2025/rust-testability-and-non-send-types/" rel="alternate" type="text/html" title="A Tale of Testability and Sending Non-Send Types in Rust" /><published>2025-05-24T00:00:00+00:00</published><updated>2025-05-24T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2025/rust-testability-and-non-send-types</id><content type="html" xml:base="https://geo-ant.github.io/blog/2025/rust-testability-and-non-send-types/"><![CDATA[<p>This is a story of testability, multithreading, and the age old question of
<em>how do we send a</em> <code class="language-plaintext highlighter-rouge">!Send</code> <em>type in Rust</em>. I’ll explore how (not) to do this,
while rambling on about how writing obsessively testable code leads to better design.
Hot takes incoming.</p>

<h1 id="testability-and-multithreading">Testability and Multithreading</h1>

<p>Let’s take a few steps back and set the scene: say we have a function that
spawns a thread. We’ll keep it simple –trivial even– to focus on the essentials.
Let’s say that all this thread does, is play some audio<sup id="fnref:trivial-example" role="doc-noteref"><a href="#fn:trivial-example" class="footnote" rel="footnote">1</a></sup>. For a
first try, we could structure our code like so:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">SystemAudio</span> <span class="p">{</span><span class="cm">/* ...*/</span><span class="p">}</span>

<span class="k">impl</span> <span class="n">SystemAudio</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="cm">/* config */</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="cm">/*...*/</span>
    <span class="p">}</span>
    <span class="k">fn</span> <span class="nf">play_music</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="cm">/* ...*/</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// ❓ how do we test this??</span>
<span class="k">fn</span> <span class="nf">spawn_thread</span><span class="p">(</span><span class="n">audio</span><span class="p">:</span> <span class="n">SystemAudio</span><span class="p">)</span> <span class="p">{</span>
    <span class="nn">std</span><span class="p">::</span><span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
        <span class="n">audio</span><span class="nf">.play_music</span><span class="p">();</span>
    <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>To my mind, there’s a problem with this code and it stems from the fact that
I am a huge nut for testability. I need to make sure that I can test the
behavior of that function. The least I want to do, is make sure that the
<code class="language-plaintext highlighter-rouge">play_music</code> function of the <code class="language-plaintext highlighter-rouge">audio</code> instance really gets called.</p>

<p>One great way to make all kinds of code testable is <a href="https://en.wikipedia.org/wiki/Dependency_inversion_principle">dependency inversion</a>,
which boils down to coding against interfaces rather than concrete types<sup id="fnref:solid" role="doc-noteref"><a href="#fn:solid" class="footnote" rel="footnote">2</a></sup>. So
rather than passing in our <code class="language-plaintext highlighter-rouge">SystemAudio</code> instance directly, we’ll
define a trait <code class="language-plaintext highlighter-rouge">Audio</code> to abstract over the behavior of the audio backend.
This allows us to mock the audio backend for testing. During testing, we can
pass in our mock to make sure that the correct behavior was indeed invoked. So
let’s refactor our code:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="n">Audio</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">play_music</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="n">SystemAudio</span> <span class="p">{</span><span class="cm">/* ...*/</span><span class="p">}</span>

<span class="k">impl</span> <span class="n">SystemAudio</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="cm">/* config */</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="cm">/*...*/</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Audio</span> <span class="k">for</span> <span class="n">SystemAudio</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">play_music</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="cm">/* ...*/</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// ⚡ this doesn't compile</span>
<span class="k">fn</span> <span class="n">spawn_thread</span><span class="o">&lt;</span><span class="n">A</span><span class="o">&gt;</span><span class="p">(</span><span class="n">audio</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span>
    <span class="k">where</span> <span class="n">A</span><span class="p">:</span> <span class="n">Audio</span> <span class="p">{</span>
    <span class="nn">std</span><span class="p">::</span><span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
        <span class="n">audio</span><span class="nf">.play_music</span><span class="p">();</span>
    <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You probably already knew this wouldn’t compile. The compiler will correctly
complain that <code class="language-plaintext highlighter-rouge">A</code> is neither <code class="language-plaintext highlighter-rouge">Send</code> nor <code class="language-plaintext highlighter-rouge">'static</code>, which is what we’d need
to move it into the thread. If we can further restrict <code class="language-plaintext highlighter-rouge">A</code> to be <code class="language-plaintext highlighter-rouge">Send</code> and
<code class="language-plaintext highlighter-rouge">'static</code>, that’s a fine solution and that would be the end of this article.
But what if we <em>can’t</em>? What if our production audio backend really does not
implement <code class="language-plaintext highlighter-rouge">Send</code>?</p>

<h1 id="how-do-we-impl-send-for-a-send-type">How Do We impl Send for a !Send Type?</h1>

<figure>
 <img src="/blog/images/rust-testing-send/you-dont-2.jpg" alt="Visualization of the FT and DTFT" style="max-width:100%" />
 <figcaption>The answer to the section heading in a nutshell.</figcaption>
</figure>

<p>If we’ve written a type <code class="language-plaintext highlighter-rouge">T</code>, where the compiler doesn’t automatically implement
<code class="language-plaintext highlighter-rouge">Send</code> but <em>we know it would be sound to do so</em>, we can of course <a href="https://doc.rust-lang.org/nomicon/send-and-sync.html">implement <code class="language-plaintext highlighter-rouge">Send</code></a>
manually and call it a day<sup id="fnref:pointers" role="doc-noteref"><a href="#fn:pointers" class="footnote" rel="footnote">3</a></sup>. However, what if the compiler doesn’t implement
<code class="language-plaintext highlighter-rouge">Send</code> on our type for a good reason? Let’s say we’re using a field of foreign type
that is itself not <code class="language-plaintext highlighter-rouge">Send</code>. We’d better assume that the crate authors deliberately did not implement
<code class="language-plaintext highlighter-rouge">Send</code> on that type. Thus, just overriding their decision by manually implementing <code class="language-plaintext highlighter-rouge">Send</code> on our
type might be unsound. So that’s out of the question, unless we have a <em>very good</em> reason
to believe it’s sound.</p>

<h2 id="using-mutext-and-arcmutext">Using <code class="language-plaintext highlighter-rouge">Mutex&lt;T&gt;</code> and <code class="language-plaintext highlighter-rouge">Arc&lt;Mutex&lt;T&gt;&gt;</code></h2>

<p>Why don’t we just wrap it in a <code class="language-plaintext highlighter-rouge">Mutex&lt;T&gt;</code>? Or an <code class="language-plaintext highlighter-rouge">Arc&lt;Mutex&lt;T&gt;&gt;</code> for good measure?
That usually helps with all kinds of multithreading-induced compile errors, right?
The unfortunate truth is that <code class="language-plaintext highlighter-rouge">Mutex&lt;T&gt;</code> is <code class="language-plaintext highlighter-rouge">Send</code> <a href="https://doc.rust-lang.org/std/sync/struct.Mutex.html#impl-Send-for-Mutex%3CT%3E">if and only if</a>
<code class="language-plaintext highlighter-rouge">T</code> is <code class="language-plaintext highlighter-rouge">Send</code>, so sticking a non-<code class="language-plaintext highlighter-rouge">Send</code> type into a mutex won’t make it <code class="language-plaintext highlighter-rouge">Send</code>.
<a href="https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT,+A%3E">The same</a>
is true for <code class="language-plaintext highlighter-rouge">Arc&lt;T&gt;</code> and thus for <code class="language-plaintext highlighter-rouge">Arc&lt;Mutex&lt;T&gt;&gt;</code>, so that won’t help either<sup id="fnref:mutex-sync" role="doc-noteref"><a href="#fn:mutex-sync" class="footnote" rel="footnote">4</a></sup>.</p>

<h2 id="interlude-isnt-there-a-crate-for-that">Interlude: Isn’t There a Crate for That?</h2>

<p>The short answer is: No, I don’t think so, let’s have a look on crates.io:</p>
<ul>
  <li><a href="`https://crates.io/crates/mutex-extra`"><code class="language-plaintext highlighter-rouge">mutex-extra</code></a>: aims to create a <code class="language-plaintext highlighter-rouge">Send</code>
type from a non-<code class="language-plaintext highlighter-rouge">Send</code> type. Diplays a “my code is erroneous, don’t use”
warning. I guess we won’t be using that then.</li>
  <li><a href="https://crates.io/crates/send_cells"><code class="language-plaintext highlighter-rouge">send_cells</code></a>: a pretty recent crate
claiming to be an alternative to <code class="language-plaintext highlighter-rouge">fragile</code> (see below).</li>
  <li><a href="https://crates.io/crates/sendable"><code class="language-plaintext highlighter-rouge">sendable</code></a>: I believe this crate is concerned
with sharing resources, rather than moving values across threads. We might
be able to use it for our purpose, but it would only give us runtime guarantees
that we didn’t do anything wrong.</li>
  <li><a href="https://crates.io/crates/send-cell"><code class="language-plaintext highlighter-rouge">send-cell</code></a>: deprecated in favor of
<code class="language-plaintext highlighter-rouge">fragile</code>.</li>
  <li><a href="https://crates.io/crates/fragile"><code class="language-plaintext highlighter-rouge">fragile</code></a>: “wrap a value and provide a <code class="language-plaintext highlighter-rouge">Send</code> bound.
Neither of the types permit access to the enclosed value unless the
thread that wrapped the value is attempting to access it”. That’s the
opposite of what we want.</li>
</ul>

<p><del>Please tell me if I’m wrong about this: for fundamental reasons, I can’t imagine a
crate existing that gives us <code class="language-plaintext highlighter-rouge">Send</code> wrappers for non-<code class="language-plaintext highlighter-rouge">Send</code> types without using
<code class="language-plaintext highlighter-rouge">unsafe</code> code that would be equivalent to implementing <code class="language-plaintext highlighter-rouge">Send</code> ourselves. We’ve
already ruled that out for good reason.</del></p>

<p><strong>Update (2025-05-26)</strong>: Fellow Rustacean <a href="https://crates.io/users/Skepfyr">Skepfyr</a>
pointed out their <a href="https://crates.io/crates/diplomatic-bag"><code class="language-plaintext highlighter-rouge">diplomatic-bag</code></a> crate
to me, which does the thing I said couldn’t be done. It comes with
<a href="https://docs.rs/diplomatic-bag/0.3.1/diplomatic_bag/struct.DiplomaticBag.html">some caveats</a>
that are well documented. I’d still argue that the solution proposed in this
article leads to an overall better design than just making a <code class="language-plaintext highlighter-rouge">!Send</code> type <code class="language-plaintext highlighter-rouge">Send</code>,
but that might just be my propensity for obsessive testing speaking.</p>

<h1 id="why-even-test-spawn_thread">Why Even Test <code class="language-plaintext highlighter-rouge">spawn_thread</code>?</h1>

<p>Yes, why even do that if the Rust compiler makes it so hard? Even if we
generally agree on the value of testing, we might be tempted to refactor our
code like so:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">spawn_thread</span><span class="p">()</span> <span class="p">{</span>
    <span class="nn">std</span><span class="p">::</span><span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(||</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">audio</span> <span class="o">=</span> <span class="nn">SystemAudio</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="cm">/*config*/</span><span class="p">);</span>
        <span class="nf">execute_thread</span><span class="p">(</span><span class="n">audio</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="n">execute_thread</span><span class="o">&lt;</span><span class="n">A</span><span class="p">:</span> <span class="n">Audio</span><span class="o">&gt;</span><span class="p">(</span><span class="n">audio</span><span class="p">:</span> <span class="n">A</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">audio</span><span class="nf">.play_music</span><span class="p">();</span>
    <span class="cm">/* and all other logic...*/</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, we can write a nice test for <code class="language-plaintext highlighter-rouge">execute_thread</code>, since that still depends
on an interface, rather than a concrete type. Isn’t this basically just as
good as testing <code class="language-plaintext highlighter-rouge">spawn_thread</code>? After all, <code class="language-plaintext highlighter-rouge">spawn_thread</code> only calls
<code class="language-plaintext highlighter-rouge">execute_thread</code> with a <code class="language-plaintext highlighter-rouge">SystemAudio</code> instance, which we want to use in
production anyway. Call me crazy, but I’ll argue this is not good enough.</p>

<p>To my mind, we should treat items under test as black boxes as much as is
feasible. Testing <code class="language-plaintext highlighter-rouge">execute_thread</code> as a substitute for <code class="language-plaintext highlighter-rouge">spawn_thread</code> relies
on the knowledge that <code class="language-plaintext highlighter-rouge">spawn_thread</code> only calls <code class="language-plaintext highlighter-rouge">execute_thread</code> in a new
thread. But from a testing point of view, that’s an implementation detail we
shouldn’t care about. We should instead be interested in making sure that
<code class="language-plaintext highlighter-rouge">spawn_thread</code> does the right thing. This might seem overly pedantic, but
imagine someone else<sup id="fnref:someone-else" role="doc-noteref"><a href="#fn:someone-else" class="footnote" rel="footnote">5</a></sup> editing our <code class="language-plaintext highlighter-rouge">spawn_thread</code> and inadvertendly introducing a bug.
I personally would want to have a test for <code class="language-plaintext highlighter-rouge">spawn_thread</code> that flags if something
unexpected happens, to catch errors higher up the chain.</p>

<p>For me, that’s the most important thing. It’s not about getting a couple lines more test coverage,
but it’s about testing the behavior of the things that are actually used… at
<em>all</em> levels of integration<sup id="fnref:downstream-tests" role="doc-noteref"><a href="#fn:downstream-tests" class="footnote" rel="footnote">6</a></sup>. I’ll one up myself and also
claim that putting in the effort to make <code class="language-plaintext highlighter-rouge">spawn_thread</code> testable leads to a better design.</p>

<h1 id="advanced-dependency-inversion">Advanced Dependency Inversion</h1>

<p>Don’t get me wrong, <em>if</em> it’s possible to restrict our type to be <code class="language-plaintext highlighter-rouge">Send + 'static</code>,
we should definitely do that and move it into the thread, like we initially
intended. We just have to come to terms with the fact that we simply <em>can’t
do that</em> in this scenario. To overcome this, we can take inspiration from
the previous section. What we did there, was to create the <code class="language-plaintext highlighter-rouge">audio</code> instance
in the thread itself, rather than move it into the thread from the outside.</p>

<p>Let’s expand on this idea: rather than hardcode the creation of the <code class="language-plaintext highlighter-rouge">audio</code>
instance in the thread itself, we create an API to inject some <em>code to create
the instance for us</em>. This sounds more complicated than it is, and
there are many ways to do that<sup id="fnref:constructor" role="doc-noteref"><a href="#fn:constructor" class="footnote" rel="footnote">7</a></sup>. I like this one:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">spawn_thread</span><span class="o">&lt;</span><span class="n">F</span><span class="p">,</span><span class="n">A</span><span class="o">&gt;</span><span class="p">(</span><span class="n">audio_constructor</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span>
    <span class="k">where</span> <span class="n">F</span><span class="p">:</span> <span class="nf">FnOnce</span><span class="p">()</span><span class="k">-&gt;</span> <span class="n">A</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="k">'static</span><span class="p">,</span>
          <span class="n">A</span><span class="p">:</span> <span class="n">Audio</span> <span class="p">{</span>
    <span class="nn">std</span><span class="p">::</span><span class="nn">thread</span><span class="p">::</span><span class="nf">spawn</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">audio</span> <span class="o">=</span> <span class="nf">audio_constructor</span><span class="p">();</span>
        <span class="n">audio</span><span class="nf">.play_music</span><span class="p">();</span>
    <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Instead of passing in the <code class="language-plaintext highlighter-rouge">audio</code> instance itself, we now pass in a callable
that constructs the instance. The callable <code class="language-plaintext highlighter-rouge">F</code> itself is restricted on <code class="language-plaintext highlighter-rouge">Send + 'static</code>,
but <code class="language-plaintext highlighter-rouge">A</code> is <em>not</em> restricted on either of these bounds. I’ve created a slighty
more involved <a href="https://play.rust-lang.org/?version=nightly&amp;mode=debug&amp;edition=2021&amp;gist=4860d3857078bb5b69c3cf704818c743">example on the playground</a>
that illustrates the use. Here’s what we can do now:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// passing a function</span>
<span class="nf">spawn_thread</span><span class="p">(</span><span class="nn">SystemAudio</span><span class="p">::</span><span class="n">default</span><span class="p">);</span>
<span class="c1">// a callable that returns a mocking backend </span>
<span class="nf">spawn_thread</span><span class="p">(||</span><span class="n">MockAudio</span><span class="p">);</span>
<span class="c1">// passing in a configuration</span>
<span class="k">let</span> <span class="n">config</span> <span class="o">=</span> <span class="n">AudioConfiguration</span> <span class="p">{</span><span class="n">device</span><span class="p">:</span> <span class="mi">123</span><span class="p">,</span> <span class="n">volume</span><span class="p">:</span> <span class="mf">0.5</span><span class="p">};</span> 
<span class="nf">spawn_thread</span><span class="p">(</span><span class="k">move</span> <span class="p">||</span><span class="nn">SystemAudio</span><span class="p">::</span><span class="nf">with_config</span><span class="p">(</span><span class="n">config</span><span class="p">));</span>
</code></pre></div></div>

<p>Using the new API like this is almost as simple as passing in
the instance itself, and it makes <code class="language-plaintext highlighter-rouge">spawn_thread</code> completely testable.
One problem we have to deal with is constructor failure, meaning the constructor
function might return a <code class="language-plaintext highlighter-rouge">Result&lt;A,E&gt;</code> rather than an <code class="language-plaintext highlighter-rouge">A</code> directly. We have to make the
thread communicate the error to the outside. However, that problem isn’t unique
to this approach, so I’ll leave it at that.</p>

<h1 id="why-its-better">Why It’s Better</h1>

<p>In this last section, let me defend my claim that this design is better,
<em>not only</em> because it makes <code class="language-plaintext highlighter-rouge">spawn_thread</code> testable. This design also decouples
the implementation of <code class="language-plaintext highlighter-rouge">spawn_thread</code> from the actual audio backend again, by
using dependency inversion. This means that we can use different audio backends
either at runtime (by refactoring to <code class="language-plaintext highlighter-rouge">dyn Audio</code>), or at compile time e.g. for
different operating systems or hardware.</p>

<p>I believe the benefits of this approach completely justify the additional complexity…
even if this starts looking a bit like a factory pattern. It’s no
<a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/AbstractFactoryBean.html"><code class="language-plaintext highlighter-rouge">AbstractFactoryBean&lt;T&gt;</code></a>, though.</p>

<h1 id="endnotes">Endnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:trivial-example" role="doc-endnote">
      <p>Since this example is so trivial, there are other ways to go about testing it. But I want to focus on the bare essentials of the problem and I ask you to bear with me, dear reader. <a href="#fnref:trivial-example" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:solid" role="doc-endnote">
      <p>Say what you will about SOLID, but dependency inversion is the hill I am willing to die on. <a href="#fnref:solid" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:pointers" role="doc-endnote">
      <p>This commonly happens, e.g. if our type contains a raw pointer field. <a href="#fnref:pointers" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:mutex-sync" role="doc-endnote">
      <p>Matters would be different if we were interested in implementing <code class="language-plaintext highlighter-rouge">Send</code> <em>and</em> <code class="language-plaintext highlighter-rouge">Sync</code> on a type <code class="language-plaintext highlighter-rouge">T</code> that <em>only</em> implements <code class="language-plaintext highlighter-rouge">Send</code>. In this case, reachig for <code class="language-plaintext highlighter-rouge">Mutex&lt;T&gt;</code> is a solution. <a href="#fnref:mutex-sync" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:someone-else" role="doc-endnote">
      <p>Yes, those pesky <em>other</em> developers. Of course <em>we</em> would never introduce a bug ourselves, right? ;) <a href="#fnref:someone-else" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:downstream-tests" role="doc-endnote">
      <p>Don’t get me wrong: I am not suggesting to only test the highest, most integrated, levels of our code. It’s good to test <code class="language-plaintext highlighter-rouge">execute_thread</code> in our example. But, to my mind, that should not absolve us from having to test <code class="language-plaintext highlighter-rouge">spawn_thread</code>, too. <a href="#fnref:downstream-tests" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:constructor" role="doc-endnote">
      <p>We could also extend the <code class="language-plaintext highlighter-rouge">Audio</code> trait to provide a constructor. I don’t like this quite as much, because different implementors of the <code class="language-plaintext highlighter-rouge">Audio</code> trait might need different arguments for construction. <a href="#fnref:constructor" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="generics" /><category term="design-patterns" /><category term="testing" /><summary type="html"><![CDATA[This is a story of testability, multithreading, and the age old question of how do we send a !Send type in Rust. I’ll explore how (not) to do this, while rambling on about how writing obsessively testable code leads to better design. Hot takes incoming.]]></summary></entry><entry><title type="html">Solving Variable Projection with QR Decomposition</title><link href="https://geo-ant.github.io/blog/2025/qr-based-varpro/" rel="alternate" type="text/html" title="Solving Variable Projection with QR Decomposition" /><published>2025-03-16T00:00:00+00:00</published><updated>2025-03-16T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2025/qr-based-varpro</id><content type="html" xml:base="https://geo-ant.github.io/blog/2025/qr-based-varpro/"><![CDATA[<p>This article continues the series on Variable Projection and
explains how to rewrite the problem using QR decomposition, rather than the more
computationally expensive singular value decomposition (SVD).</p>

<h1 id="context">Context</h1>

<p>VarPro is an algorithm to perform nonlinear least squares fitting for a
certain class of model functions to observations<sup id="fnref:fitting" role="doc-noteref"><a href="#fn:fitting" class="footnote" rel="footnote">1</a></sup>. I’ve
written about the <a href="/blog/2020/variable-projection-part-1-fundamentals/">fundamentals of varpro</a>
and its <a href="/blog/2024/variable-projection-part-2-multiple-right-hand-sides/">extension to global fitting</a>
–as well as <a href="/blog/tags/#varpro">related topics</a>– on this blog. I also maintain
the free and open-source <a href="https://crates.io/crates/varpro"><code class="language-plaintext highlighter-rouge">varpro</code></a> library in the Rust
language. This article is part of a long-running series on Variable Projection (VarPro)
on this blog, so I will rush through a lot of the fundamentals. I assume prior
knowledge of the first two linked articles, and I’ll be using the same notation.</p>

<h1 id="goal">Goal</h1>

<p>The core computations in my library use the SVD matrix decomposition. While this makes
the algorithm very robust, it also comes with a speed penalty for problems that
don’t actually require this degree of robustness. There is a well-known way to speed up
the calculations by using the QR decomposition and some mathematical sleights
of hand. In fact, that approach is used in many other implementations
and it’s the original idea of Linda Kaufman, published in (Kau75). This article explains
that approach. See also (Bae23) for a great recap as well as some cool extensions
to global fitting<sup id="fnref:bae-qr" role="doc-noteref"><a href="#fn:bae-qr" class="footnote" rel="footnote">2</a></sup>.</p>

<h1 id="varpro-with-qr">VarPro with QR</h1>

<p>From the previous articles, we know that we can write a separable model function
\(\boldsymbol{f}\), which depends on the parameters \(\boldsymbol{c}\) linearly
and on the parameters \(\boldsymbol{\alpha}\) nonlinearly, as</p>

\[\boldsymbol{f}(\boldsymbol{c},\boldsymbol{\alpha}) = \boldsymbol{\Phi}(\boldsymbol{\alpha})\boldsymbol{c}, \label{f} \tag{1}\]

<p>where we call \(\boldsymbol{\Phi}(\boldsymbol{\alpha}) \in \mathbb{R}^{m \times n}\),
with \(m&gt;n\), the model function matrix. Typically, we are interested in some
form of <em>weighting</em> for the least squares problem, so that we end up with
the weighted function matrix \(\boldsymbol{\Phi}_w = \boldsymbol{W} \boldsymbol{\Phi} \in \mathbb{R}^{m \times n}\)
and the weighted matrix of observations \(\boldsymbol{Y}_w = \boldsymbol{W Y}\).
After some neat math, which is described in detail in the previous articles, we arrive 
at this functional to minimize:</p>

\[\begin{eqnarray}
R_{WLS}(\boldsymbol{\alpha})
  &amp;=&amp;\Vert \boldsymbol{Y}_w - \Phi_w(\boldsymbol{\alpha}) \boldsymbol{\hat{C}}(\boldsymbol{\alpha})\Vert_F^2 \label{weighted-diff} \tag{2} \\[5pt]
  &amp;=&amp;\Vert \boldsymbol{P}^\perp_{\boldsymbol{\Phi_w}(\boldsymbol{\alpha})} \boldsymbol{Y}_w \Vert_F^2 \label{functional} \tag{3} \\[5pt]
\boldsymbol{\hat{C}}(\boldsymbol{\alpha}) &amp;:=&amp; \Phi_w^\dagger(\boldsymbol{\alpha}) \boldsymbol{Y}_w, \label{chat} \tag{4}
\end{eqnarray}\]

<p>which only depends on \(\boldsymbol{\alpha}\). \(R_{WLS}(\boldsymbol{\alpha})\) is the sum of squared residuals we
want to minimize and \(\lVert . \rVert_F^2\) is the
<a href="https://mathworld.wolfram.com/FrobeniusNorm.html">Frobenius Norm</a>.
\(R_{WLS}(\boldsymbol{\alpha})\) is also called the <em>projection functional</em> and the matrix
\(\boldsymbol{P}^\perp_{\boldsymbol{\Phi_w}(\boldsymbol{\alpha})} \in \mathbb{R}^{m \times m}\) is the
<em>projection onto the orthogonal complement of the range of</em> \(\boldsymbol{\Phi}_w(\boldsymbol{\alpha})\).
For this article, I have used the formulation of VarPro that can account for
multiple <a href="/blog/2024/variable-projection-part-2-multiple-right-hand-sides/">right hand sides</a>,
but the whole article is also applicable to VarPro with a
<a href="/blog/2024/variable-projection-part-2-multiple-right-hand-sides/">single right hand side</a>.
In that case, the matrix \(Y_w\) becomes the observation <em>vector</em> \(\boldsymbol{y}_w\)
and the matrix norm \(\lVert . \rVert_F^2\) becomes the
<a href="https://en.wikipedia.org/wiki/Euclidean_space#Euclidean_norm">euclidean norm</a>
\(\lVert . \rVert_2^2\).</p>

<p>In the previous articles, we have used the singular value decomposition of
\(\boldsymbol{\Phi}_w(\boldsymbol{\alpha})\) to rewrite the functional.
Now, we want to use the QR-decomposition with column-pivoting of the matrix \(\boldsymbol{\Phi}_w\).
I’ll leave out the dependency on \(\boldsymbol{\alpha}\)
for notational simplicity from now on, but all matrices in the following equation
have a dependency on the vector of nonlinear parameters \(\boldsymbol{\alpha}\).
The column-pivoted QR decomposition of \(\boldsymbol{\Phi}_w\) exists so that<sup id="fnref:kaufmann-qr" role="doc-noteref"><a href="#fn:kaufmann-qr" class="footnote" rel="footnote">3</a></sup>:</p>

\[\boldsymbol{\Phi}_w\boldsymbol{\Pi} = \boldsymbol{Q} \boldsymbol{R} \label{qr} \tag{5},\]

<p>where \(\boldsymbol{\Pi}\in \mathbb{R}^{n\times n}\) is a permutation matrix
that reorders the columns of \(\boldsymbol{\Phi_w}\) for numerical stability,
\(\boldsymbol{Q} \in \mathbb{R}^{m \times m}\) is an <em>orthogonal</em> matrix and
\(\boldsymbol{R}\) is a matrix of this form:</p>

\[\boldsymbol{R} = \left[
\begin{array}{c|c}
\boldsymbol{R_1} &amp; \boldsymbol{R_2} \\
\hline
\boldsymbol{0} &amp; \boldsymbol{0} \\
\end{array}
\right] \in \mathbb{R}^{m \times n}, \label{r} \tag{6}\]

<p>where \(\boldsymbol{R_1} \in \mathbb{R}^{r \times r}\) is a nonsingular upper triangular
matrix with \(r = \text{rank} \boldsymbol{\Phi}_w\). The matrix \(\boldsymbol{R}_2 \in \mathbb{R}^{r \times (n-r)}\)
contains values that we won’t need for the rest of this article<sup id="fnref:r2-fullrank" role="doc-noteref"><a href="#fn:r2-fullrank" class="footnote" rel="footnote">4</a></sup>. It is useful
to divide \(\boldsymbol{Q}\) into two sub-matrices as well like so:</p>

\[\boldsymbol{Q} = 
\left[
\begin{array}{c|c}
\boldsymbol{Q}_1 &amp; \boldsymbol{Q}_2
\end{array}
\right], \label{q} \tag{7}\]

<p>where \(\boldsymbol{Q}_1 \in \mathbb{R}^{m \times r}\) is the submatrix of the
first \(r\) columns, and \(\boldsymbol{Q}_2 \in \mathbb{R}^{m \times (m-r)}\)
contains the rest. Kaufman gives expressions for the pseudoinverse
\(\boldsymbol{\Phi}_w^\dagger \in \mathbb{R}^{n \times m}\) of the function
matrix, and for the projection matrix using the QR decomposition (Kau75):</p>

\[\begin{eqnarray}
  \boldsymbol{P}^\perp_{\boldsymbol{\Phi_w}(\boldsymbol{\alpha})} &amp;=&amp; 
  \boldsymbol{Q}
  \underbrace{
  \left[
  \begin{array}{c|c}
  \boldsymbol{0} &amp; \boldsymbol{0} \\
  \hline
  \boldsymbol{0} &amp; \boldsymbol{I}_{(m-r)\times(m-r)} \\
  \end{array}
  \right]
  }_{m \times m}
  \boldsymbol{Q}^T \\
  &amp;=&amp;\boldsymbol{Q}
  \left[
  \begin{array}{c}
  \boldsymbol{0}_{r\times m} \\
  \hline
  \boldsymbol{Q}_2^T \\
  \end{array}
  \right]
  \label{p-qiq}
  \tag{8}\\[10pt]
  \boldsymbol{\Phi_w}^\dagger &amp;=&amp; 
  \boldsymbol{\Pi}
  \underbrace{
  \left[
  \begin{array}{c|c}
  \boldsymbol{R_1}^{-1} &amp; \boldsymbol{0} \\
  \hline
  \boldsymbol{0} &amp; \boldsymbol{0} \\
  \end{array}
  \right]
  }_{n \times m}
  \boldsymbol{Q}^T \\
  &amp;=&amp; \boldsymbol{\Pi}
  \left[\begin{array}{c}
  \boldsymbol{R_1}^{-1} \boldsymbol{Q_1}^T \\
  \hline
  \boldsymbol{0}_{(n-r)\times m} \\
  \end{array}
  \right]
  . \label{phi-dagger} \tag{9}
\end{eqnarray}\]

<p>Note, that if \(\boldsymbol{\Phi}_w\) has full rank, i.e. \(r = n\), then
the middle block matrix becomes \(\left[\boldsymbol{R}_1^{-1} \vert \boldsymbol{0}\right]\)
and thus \(\boldsymbol{\Phi}_w^\dagger = \boldsymbol{\Pi} \boldsymbol{R}_1^{-1}\boldsymbol{Q}_1^T\).
Plugging \(\eqref{p-qiq}\) into \(\eqref{functional}\) lets us write</p>

\[\begin{eqnarray}
R_{WLS}(\boldsymbol{\alpha}) &amp;=&amp; \left\Vert \boldsymbol{Q}
  \left[
  \begin{array}{c}
  \boldsymbol{0} \\
  \hline
  \boldsymbol{Q}_2^T \\
  \end{array}
  \right]
  \boldsymbol{Y}_w \right\Vert_F^2\\[5pt]
  &amp;=&amp;
  \left\Vert 
  \left[
  \begin{array}{c}
  \boldsymbol{0} \\
  \hline
  \boldsymbol{Q}_2^T \\
  \end{array}
  \right]
  \boldsymbol{Y}_w \right\Vert_F^2  \\[5pt]
  &amp;=&amp;
  \left\Vert 
  \boldsymbol{Q}_2^T 
  \boldsymbol{Y}_w \right\Vert_F^2.
  \label{functional-q} \tag{10}
\end{eqnarray}\]

<p>For the first step, we have used the fact that we can just drop \(\boldsymbol{Q}\)
due to  invariance of the norm under orthogonal transformations<sup id="fnref:norm-ortho" role="doc-noteref"><a href="#fn:norm-ortho" class="footnote" rel="footnote">5</a></sup>.
The functional we want to minimize is thus</p>

\[R_{WLS}(\boldsymbol{\alpha}) =\left\Vert 
\boldsymbol{Q}_2^T(\boldsymbol{\alpha})
\boldsymbol{Y}_w \right\Vert_F^2.
\label{functional-q2} \tag{11}\]

<p>To use off-the-shelf minimization algorithms, like Levenverg-Marquardt, we
have to calculate the Jacobian matrix. I have given expressions for that
in the linked articles for single and multiple right hand sides. Instead of
the partial derivatives for \(\boldsymbol{P}^\perp_{\boldsymbol{\Phi_w}(\boldsymbol{\alpha})}\),
we now need an expression for the partial derivatives of \(\boldsymbol{Q}_2^T(\boldsymbol{\alpha})\).
Luckily, Kaufman gives an approximation for this expression:</p>

\[\frac{\partial \boldsymbol{Q}_2^T}{\alpha_k} \approx -\boldsymbol{Q}_2^T\frac{\partial \boldsymbol{\Phi_w}}{\alpha_k} \boldsymbol{\Phi_w}^\dagger, \label{dQ2dak} \tag{12}\]

<p>with \(\boldsymbol{\Phi}^\dagger\) as in \(\eqref{phi-dagger}\). There is a
typo in the Kaufman paper for the final form of this equation, which
leaves out the preceding minus (\(-\)). The previous formulas in (Kau75), and the derivation
in (Bae23) show that this is an oversight.</p>

<h2 id="a-note-on-implementation">A Note on Implementation</h2>

<p>Although we’ve got all the formulas we need now, I’ll give some additional
notes on the implementation. Using the same way we derived an expression for the \(k\)-th
column \(\boldsymbol{j}_k\) of the Jacobian in the previous articles, we can now
write</p>

\[\boldsymbol{j}_k = \text{vec} \left(\frac{\partial \boldsymbol{Q}_2^T}{\partial \alpha_k} \boldsymbol{Y}_w\right) \tag{13}\]

<p>for the case of multiple right hand sides<sup id="fnref:single-rhs-jac" role="doc-noteref"><a href="#fn:single-rhs-jac" class="footnote" rel="footnote">6</a></sup>. Using \(\eqref{dQ2dak}\)
in the expression above leads us to</p>

\[\begin{eqnarray}
\boldsymbol{j}_k &amp;=&amp; \text{vec} \left(-\boldsymbol{Q}_2^T\frac{\partial \boldsymbol{\Phi_w}}{\alpha_k} \boldsymbol{\Phi_w}^\dagger \boldsymbol{Y}_w\right) \\[5pt] 
 &amp;=&amp; \text{vec} \left(-\boldsymbol{Q}_2^T\frac{\partial \boldsymbol{\Phi_w}}{\alpha_k} \boldsymbol{\hat{C}}\right), \\[5pt] 
\end{eqnarray}\]

<p>with \(\boldsymbol{\hat{C}}\) as in \(\eqref{chat}\) being the least squares solution
to the <em>linear</em> least squares problem of minimizing \(\eqref{weighted-diff}\) for fixed
\(\boldsymbol{\alpha}\). When using linear algebra libraries to calculate the
QR decomposition, this solution can typically be obtained without having to
create the pseudoinverse ourselves.</p>

<p>Since we can get \(\boldsymbol{\hat{C}}\) from the linear algebra backend, which we’re
(probably) using, we should also consider whether it’s cheaper to calculate
\(R_{WLS}\) using \(\eqref{weighted-diff}\) or using \(\eqref{functional-q}\).
This likely depends on the details of the linear algebra backend.</p>

<h1 id="conclusion">Conclusion</h1>

<p>This is all we need to make VarPro use the column-pivoted QR decomposition
instead of the SVD. Using the QR decomposition <em>with</em> column-pivoting will work even
if the matrix \(\boldsymbol{\Phi}_w(\boldsymbol{\alpha})\) becomes singular
or near-singular while iterating for a solution. If it’s safe to assume that this
won’t be the case <em>for all iterations</em>, then we can use the QR decomposition
without column pivoting. This should be even faster to compute, and it’s 
what (Bae23) and (Mul08) do. This leads to virtually the same formulas as presented
above with the difference that there is no permutation matrix
(\(\boldsymbol{\Pi}=\boldsymbol{I}\)) and \(r = \text{rank}(\boldsymbol{\Phi}_w) = n\),
which implies \(\boldsymbol{Q}_2 \in \mathbb{R}^{m \times (m-n)}\).</p>

<h1 id="references-and-further-reading">References and Further Reading</h1>

<p><strong>(Bae23)</strong> Bärligea, A. <em>et al.</em> (2023) “A Generalized Variable Projection
Algorithm for Least Squares Problems in Atmospheric Remote Sensing,”
<em>Mathematics</em> <strong>2023, 11, 2839</strong> <a href="https://doi.org/10.3390/math11132839">DOI link</a></p>

<p><strong>(Mul08)</strong> Mullen, K. M. (2008). “Separable nonlinear models: theory,
implementation and applications in physics and chemistry.”
<a href="https://research.vu.nl/ws/portalfiles/portal/75843866/complete%20dissertation.pdf">PhD-Thesis - Research and graduation internal, Vrije Universiteit Amsterdam</a>.</p>

<p><strong>(Kau75)</strong> Kaufman, L. A variable projection method for solving
separable nonlinear least squares problems.
BIT 15, 49–57 (1975). <a href="https://doi.org/10.1007/BF01932995">DOI link</a></p>

<p><strong>(Gol73)</strong> Golub, G.; Pereyra, V. “The Differentiation of Pseudo-Inverses
and Nonlinear Least Squares Problems Whose Variables Separate”.
SIAM J. Numer. Anal. <strong>1973, 10, 413–432</strong>. <a href="https://doi.org/10.1137/0710036">DOI link</a></p>

<h1 id="endnotes">Endnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:fitting" role="doc-endnote">
      <p>VarPro isn’t strictly for function fitting only, since it’s a way of rewriting <em>separable</em> nonlinear least squares minimization problems. It’s just widely employed for model fitting, which is also what I am using it for. <a href="#fnref:fitting" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:bae-qr" role="doc-endnote">
      <p>While Kaufman uses QR decomposition with column-pivoting, Bärligea uses QR decomposition without pivoting. There are some slight changes in the formulas to watch out for. Further, the QR decomposition with column-pivoting will be more stable when the function matrix is singular or nearly singular, albeit at a somewhat higher computational cost. <a href="#fnref:bae-qr" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:kaufmann-qr" role="doc-endnote">
      <p>Note that Kaufman uses slightly different –but equivalent– convention for the QR decomposition than this article and (Bae23). This must be taken into account when comparing the equations across publications. Specifically, Kaufman gives the decomposition as \(Q\Phi P = R\), whereas (Bae23) and I use the more common \(\Phi P = QR\) convention. That’s not a big deal. It just means, that for Kaufman \(Q\) means \(Q^T\) in this article and vice versa. <a href="#fnref:kaufmann-qr" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:r2-fullrank" role="doc-endnote">
      <p>If \(\boldsymbol{\Phi}_w\) has full rank, i.e. \(r = n\), then the matrix \(\boldsymbol{R}_2\) has zero columns. <a href="#fnref:r2-fullrank" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:norm-ortho" role="doc-endnote">
      <p>Hooray for <del>margin</del> endnote proofs: the squared Frobenius norm of a matrix \(\boldsymbol{A}\) can be written as \(\Vert \boldsymbol{A}\Vert_F^2=\text{trace}(\boldsymbol{A}^T A)\). With that, it’s trivial to show that \(\Vert \boldsymbol{QA}\Vert_F^2=\Vert \boldsymbol{A}\Vert_F^2\), if \(\boldsymbol{Q}\) is orthogonal, since \(\boldsymbol{Q Q}^T = \boldsymbol{Q}^T \boldsymbol{Q} = I\). In the single right hand side case, we would have the euclidean norm of a vector instead of the Frobenius norm. The euclidean norm of a vector is also invariant under orthogonal transformation, i.e. \(\Vert \boldsymbol{Q x}\Vert_2^2 = \Vert \boldsymbol{x}\Vert_2^2\) for all orthogonal matrices \(\boldsymbol{Q}\) and all vectors \(\boldsymbol{x}\). <a href="#fnref:norm-ortho" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:single-rhs-jac" role="doc-endnote">
      <p>This just becomes \(\boldsymbol{j}_k = \frac{\partial \boldsymbol{Q}_2^T}{\partial \alpha_k} \boldsymbol{y}_w\) for the single right-hand-side case, whereas the next equation just becomes \(\boldsymbol{j}_k = -\boldsymbol{Q}_2^T\frac{\partial \boldsymbol{\Phi_w}}{\alpha_k} \boldsymbol{\hat{c}}\), where \(\boldsymbol{\hat{c}}\) is a vector rather than a matrix. <a href="#fnref:single-rhs-jac" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="least-squares" /><category term="algorithm" /><category term="varpro" /><summary type="html"><![CDATA[This article continues the series on Variable Projection and explains how to rewrite the problem using QR decomposition, rather than the more computationally expensive singular value decomposition (SVD).]]></summary></entry><entry><title type="html">Cursed Linear Types In Rust</title><link href="https://geo-ant.github.io/blog/2024/rust-linear-types-use-once/" rel="alternate" type="text/html" title="Cursed Linear Types In Rust" /><published>2024-11-27T00:00:00+00:00</published><updated>2024-11-27T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2024/rust-linear-types-use-once</id><content type="html" xml:base="https://geo-ant.github.io/blog/2024/rust-linear-types-use-once/"><![CDATA[<p>Inspired by Jack Wrenn’s post on <a href="https://jack.wrenn.fyi/blog/undroppable/">Undroppable Types</a>
in Rust, I set out to see if it’s possible to create types that must be used exactly
once. From my understanding, those things are called <em>linear types</em>, but
don’t quote me on that<sup id="fnref:title" role="doc-noteref"><a href="#fn:title" class="footnote" rel="footnote">1</a></sup>.</p>

<p>Let’s see if we can create a struct <code class="language-plaintext highlighter-rouge">UseOnce&lt;T&gt;</code> which enforces that an instance
is used (or <em>consumed</em>) exactly once. It should be impossible to consume it
more than once, and it should produce a compile error if it’s not consumed at all.
The first part is trivial with destructive move semantics, the second
part is where we <del>steal</del> adapt Jack’s original idea.</p>

<h1 id="implementation">Implementation</h1>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">core</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="n">ManuallyDrop</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">core</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="n">MaybeUninit</span><span class="p">;</span>

<span class="k">pub</span> <span class="k">struct</span> <span class="n">UseOnce</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">MaybeUninit</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">);</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">UseOnce</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="n">val</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span><span class="p">(</span><span class="nn">MaybeUninit</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">val</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="k">pub</span> <span class="k">fn</span> <span class="n">consume</span><span class="o">&lt;</span><span class="n">F</span><span class="p">,</span> <span class="n">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">R</span>
    <span class="k">where</span>
        <span class="n">F</span><span class="p">:</span> <span class="nf">FnOnce</span><span class="p">(</span><span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">R</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="c1">// (1)</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">this</span> <span class="o">=</span> <span class="nn">ManuallyDrop</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="k">self</span><span class="p">);</span>
        <span class="c1">// (2)</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">val</span> <span class="o">=</span> <span class="nn">MaybeUninit</span><span class="p">::</span><span class="nf">uninit</span><span class="p">();</span>
        <span class="nn">std</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nf">swap</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">this</span><span class="na">.0</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">val</span><span class="p">);</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">val</span> <span class="o">=</span> <span class="n">val</span><span class="nf">.assume_init</span><span class="p">();</span>
            <span class="nf">f</span><span class="p">(</span><span class="n">val</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nb">Drop</span> <span class="k">for</span> <span class="n">UseOnce</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">drop</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">const</span> <span class="p">{</span>
        <span class="nd">panic!</span><span class="p">(</span><span class="s">"UseOnce instance must be consumed!"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">instance</span> <span class="o">=</span> <span class="nn">UseOnce</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">41</span><span class="p">);</span>
    <span class="c1">// (3)</span>
    <span class="c1">// comment out this line to get a compile error</span>
    <span class="k">let</span> <span class="n">_result</span> <span class="o">=</span> <span class="n">instance</span><span class="nf">.consume</span><span class="p">(|</span><span class="n">v</span><span class="p">|</span> <span class="n">v</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=8bb04cf8311fd98e0506a1b764b72d2b">Playground Link</a>.
Again, the clever part is Jack Wrenn’s original idea. I was also surprised this
works. To my understanding, it relies on the fact that the compiler can reason
that the drop implementation does not have to be generated when <code class="language-plaintext highlighter-rouge">consume</code> is 
called due to ①. There’s some additional unsafe trickery in ②,
which is not terribly important but it’s actually safe. It allows me to use
<code class="language-plaintext highlighter-rouge">MaybeUninit&lt;T&gt;</code> instead of <code class="language-plaintext highlighter-rouge">Option&lt;T&gt;</code> as the inner type so that there’s no
space penalty as there could be if I had used an <code class="language-plaintext highlighter-rouge">Option</code>.</p>

<p>As is, the code compiles just fine, but if we comment out the <code class="language-plaintext highlighter-rouge">consume</code> below
③, it will fail with a compile error like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error[E0080]: evaluation of `&lt;UseOnce&lt;i32&gt; as std::ops::Drop&gt;::drop::{constant#0}` failed
  --&gt; src/main.rs:27:9
   |
27 |         panic!("UseOnce instance must be consumed!")
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'UseOnce instance must be consumed!', src/main.rs:27:9
   |
   = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)

note: erroneous constant encountered
  --&gt; src/main.rs:26:9
   |
26 | /         const {
27 | |         panic!("UseOnce instance must be consumed!")
28 | |         }
   | |_________^

note: the above error was encountered while instantiating `fn &lt;UseOnce&lt;i32&gt; as std::ops::Drop&gt;::drop`
   --&gt; /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:574:1
    |
574 | pub unsafe fn drop_in_place&lt;T: ?Sized&gt;(to_drop: *mut T) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0080`.
</code></pre></div></div>

<p>Not exactly pretty but it does the trick. Note, that the compile error
is <a href="https://www.reddit.com/r/rust/comments/1h0zcku/comment/lzexqsz/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">not triggered</a>
by simply running <code class="language-plaintext highlighter-rouge">cargo check</code>, but we need to run <code class="language-plaintext highlighter-rouge">cargo build</code>.</p>

<h1 id="why-its-cursed">Why It’s Cursed</h1>

<p>Unfortunately, the <code class="language-plaintext highlighter-rouge">UseOnce&lt;T&gt;</code> is not as useful or powerful as it might seem
at first sight. Firstly, since the compiler error is enforced by the <code class="language-plaintext highlighter-rouge">Drop</code> implementation, we
can just <a href="https://doc.rust-lang.org/std/mem/fn.forget.html"><code class="language-plaintext highlighter-rouge">mem::forget</code></a> the instance
and not actually consume it. I don’t feel this is a giant problem because it’s
still very explicit and arguably counts as a sort of consumption. However,
there’s a <a href="https://www.reddit.com/r/rust/comments/1h0zcku/comment/lzaggnp/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">more fundamental problem</a>
with linear types in Rust as pointed out by <code class="language-plaintext highlighter-rouge">u/Shnatsel</code> in the reddit thread
for this post. Note also that I am using the term <em>linear types</em> somewhat loosely and incorrectly,
please see <a href="https://www.reddit.com/r/rust/comments/1h0zcku/comment/lz7xox5/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">this comment thread</a>.</p>

<p>Secondly, the presented API allows us to “exfiltrate” the inner value of the <code class="language-plaintext highlighter-rouge">UseOnce&lt;T&gt;</code> instance
by just calling <code class="language-plaintext highlighter-rouge">consume</code> with the identity function. To address this, we can
replace the implementation of <code class="language-plaintext highlighter-rouge">consume</code> by two functions like so:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="n">consume</span><span class="o">&lt;</span><span class="n">F</span><span class="p">,</span> <span class="n">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">R</span>
<span class="k">where</span>
    <span class="n">F</span><span class="p">:</span> <span class="nf">FnOnce</span><span class="p">(</span><span class="o">&amp;</span><span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">R</span><span class="p">,</span>
<span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">this</span> <span class="o">=</span> <span class="nn">ManuallyDrop</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="k">self</span><span class="p">);</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">val</span> <span class="o">=</span> <span class="nn">MaybeUninit</span><span class="p">::</span><span class="nf">uninit</span><span class="p">();</span>
    <span class="nn">std</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nf">swap</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">this</span><span class="na">.0</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">val</span><span class="p">);</span>
    <span class="k">unsafe</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">val</span> <span class="o">=</span> <span class="n">val</span><span class="nf">.assume_init</span><span class="p">();</span>
        <span class="nf">f</span><span class="p">(</span><span class="o">&amp;</span><span class="n">val</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">pub</span> <span class="k">fn</span> <span class="n">consume_mut</span><span class="o">&lt;</span><span class="n">F</span><span class="p">,</span> <span class="n">R</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="n">F</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">R</span>
<span class="k">where</span>
    <span class="n">F</span><span class="p">:</span> <span class="nf">FnOnce</span><span class="p">(</span><span class="nb">Pin</span><span class="o">&lt;&amp;</span><span class="k">mut</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">R</span><span class="p">,</span>
<span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">this</span> <span class="o">=</span> <span class="nn">ManuallyDrop</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="k">self</span><span class="p">);</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">val</span> <span class="o">=</span> <span class="nn">MaybeUninit</span><span class="p">::</span><span class="nf">uninit</span><span class="p">();</span>
    <span class="nn">std</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nf">swap</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">this</span><span class="na">.0</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">val</span><span class="p">);</span>
    <span class="k">unsafe</span> <span class="p">{</span>
        <span class="k">let</span> <span class="k">mut</span> <span class="n">val</span> <span class="o">=</span> <span class="n">val</span><span class="nf">.assume_init</span><span class="p">();</span>
        <span class="k">let</span> <span class="n">pinned</span> <span class="o">=</span> <span class="nn">Pin</span><span class="p">::</span><span class="nf">new_unchecked</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">val</span><span class="p">);</span>
        <span class="nf">f</span><span class="p">(</span><span class="n">pinned</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=18cab23d9f56c50bfbae024f863233a7">Playground Link</a>.
Calling any of these functions will still consume the instance of <code class="language-plaintext highlighter-rouge">UseOnce&lt;T&gt;</code>,
but the functions only expose access to the inner value by shared or mutable
reference, respectively. The borrow checker prohibits simply passing the reference
to the outside. Note, that we have used the infamous <code class="language-plaintext highlighter-rouge">Pin</code> in the <code class="language-plaintext highlighter-rouge">consume_mut</code>
function to express that the inner value must not be moved out of this reference<sup id="fnref:unsafe" role="doc-noteref"><a href="#fn:unsafe" class="footnote" rel="footnote">2</a></sup>.</p>

<p>Thirdly, as was <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=02cc5096672900f10fb190595c6361ff">pointed out</a>
by <code class="language-plaintext highlighter-rouge">u/SkiFire13</code> in the <a href="https://www.reddit.com/r/rust/comments/1gzmwcb/undroppable_types/">original reddit thread</a>,
this trick relies on the compiler’s ability to reason 
<em>without optimizations</em> that the type will not be dropped<sup id="fnref:unspecified" role="doc-noteref"><a href="#fn:unspecified" class="footnote" rel="footnote">3</a></sup>. Thus,
simply sticking a function call between the creation and consumption of the instance
will make this code fail<sup id="fnref:panic" role="doc-noteref"><a href="#fn:panic" class="footnote" rel="footnote">4</a></sup>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">instance</span> <span class="o">=</span> <span class="nn">UseOnce</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="mi">41</span><span class="p">);</span>
    <span class="nf">foo</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">_result</span> <span class="o">=</span> <span class="n">instance</span><span class="nf">.consume</span><span class="p">(|</span><span class="n">v</span><span class="p">|</span> <span class="n">v</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This code does not compile despite the value being consumed. You can see how
this severely limits the applicability of <code class="language-plaintext highlighter-rouge">UseOnce</code>. There is an even more cursed
remedy for that, which is using the idea of the <a href="https://github.com/mickvangelderen/prevent_drop">prevent_drop</a>
crate. In that crate, a non-existing external function is linked in the <code class="language-plaintext highlighter-rouge">Drop</code>
implementation, which moves the error to link time. That will make it work for
this case but it also makes the error even uglier<sup id="fnref:linker" role="doc-noteref"><a href="#fn:linker" class="footnote" rel="footnote">5</a></sup>.</p>

<h1 id="endnotes">Endnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:title" role="doc-endnote">
      <p>Unless you are quoting the title of this article which explicitly says linear types… I feel stupid now. <a href="#fnref:title" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:unsafe" role="doc-endnote">
      <p>It’s still possible to use unsafe code that violates the semantic restrictions of <code class="language-plaintext highlighter-rouge">Pin</code> to do that, though. <a href="#fnref:unsafe" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:unspecified" role="doc-endnote">
      <p>To add insult to injury, this implementation relies on <a href="https://www.reddit.com/r/rust/comments/1gzmwcb/comment/lyzj7yi/">unspecified behavior</a> of the compiler. This won’t cause runtime UB though, to my understanding. So the worst thing that can happen is that this neat trick stops compiling alltogether. Thanks to <code class="language-plaintext highlighter-rouge">u/dydhaw</code> for <a href="https://www.reddit.com/r/rust/comments/1h0zcku/comment/lz7xox5/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">mentioning this</a>. <a href="#fnref:unspecified" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:panic" role="doc-endnote">
      <p>If you want to find out why, it’s explained in the comment thread. <a href="#fnref:panic" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:linker" role="doc-endnote">
      <p>Plus it introduces the can of worms of how to know that a symbol name is never going to be actually linked. There are ways around that, but I don’t feel they’ll be pretty. <a href="#fnref:linker" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="metaprogramming" /><category term="generics" /><summary type="html"><![CDATA[Inspired by Jack Wrenn’s post on Undroppable Types in Rust, I set out to see if it’s possible to create types that must be used exactly once. From my understanding, those things are called linear types, but don’t quote me on that1. Unless you are quoting the title of this article which explicitly says linear types… I feel stupid now. &#8617;]]></summary></entry><entry><title type="html">Rethinking Builders… with Lazy Generics</title><link href="https://geo-ant.github.io/blog/2024/rust-rethinking-builders-lazy-generics/" rel="alternate" type="text/html" title="Rethinking Builders… with Lazy Generics" /><published>2024-10-11T00:00:00+00:00</published><updated>2024-10-11T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2024/rust-rethinking-builders-lazy-generics</id><content type="html" xml:base="https://geo-ant.github.io/blog/2024/rust-rethinking-builders-lazy-generics/"><![CDATA[<p>While using compile-time builder generator crates, I realized that I had run into
a niche problem that required lot more flexibility with generic structs and 
functions than I was getting. If you like, follow me down a rabbit hole 
and explore the builder pattern from a very generic-centric perspective.</p>

<h1 id="intro-to-builders">Intro To Builders</h1>

<p>The <a href="https://rust-unofficial.github.io/patterns/patterns/creational/builder.html">builder pattern</a>
in Rust is so popular that there are a lot of crates that allow us to avoid
the boilerplate of generating the builders ourselves. Probably you’re already
familiar with the builder pattern and at least one crate that allows you to automatically
derive a compile-time verified builder for structs.</p>

<p>Here’s a highly <code class="language-plaintext highlighter-rouge">#[non-exhaustive]</code> list comprising these
kind of crates and their taglines on crates.io, ordered in descending order by total
number of downloads at the time of writing:</p>

<ul>
  <li><a href="https://crates.io/crates/typed-builder"><code class="language-plaintext highlighter-rouge">typed_builder</code></a> (ver <code class="language-plaintext highlighter-rouge">0.20.0</code>) “Compile-time type-checked builder derive”</li>
  <li><a href="https://crates.io/crates/buildstructor"><code class="language-plaintext highlighter-rouge">buildstructor</code></a> (ver <code class="language-plaintext highlighter-rouge">0.5.4</code>) “[…] derive a builder from a constructor function.”</li>
  <li><a href="https://crates.io/crates/bon"><code class="language-plaintext highlighter-rouge">bon</code></a> (ver <code class="language-plaintext highlighter-rouge">2.3.0</code>) “Generate builders for everything!”</li>
  <li><a href="https://crates.io/crates/const_typed_builder"><code class="language-plaintext highlighter-rouge">const_typed_builder</code></a> (ver. <code class="language-plaintext highlighter-rouge">0.3.0</code>) “Compile-time type-checked builder derive using const generics”</li>
  <li><a href="https://crates.io/crates/typestate-builder"><code class="language-plaintext highlighter-rouge">typestate_builder</code></a> (ver <code class="language-plaintext highlighter-rouge">0.1.1</code>) “[…] combines <code class="language-plaintext highlighter-rouge">Typestate</code> and <code class="language-plaintext highlighter-rouge">Builder</code> patterns”</li>
</ul>

<p>Note, that I gave the current crate versions in brackets, at the time of writing.
So, when I reference those crates, it’ll be based on these particular
versions. All of those crates are great, with <code class="language-plaintext highlighter-rouge">bon</code> and <code class="language-plaintext highlighter-rouge">typed_builder</code> being my personal
favorites and also among the most widely used. All these crates generate compile-time validated builders,
so they will give errors <em>at compile time</em> when you forget to set a mandatory field<sup id="fnref:runtime-builders" role="doc-noteref"><a href="#fn:runtime-builders" class="footnote" rel="footnote">1</a></sup>.
The <code class="language-plaintext highlighter-rouge">typestate_builder</code> crate is an honorable mention here, that I stumbled upon after
doing the proof-of-concept implementation presented in this article. At the time
of writing it’s very young and incomplete, but it’s different from the other mentioned crates
in some important details that –I believe– would enable it to implement
the ideas presented in this article.</p>

<p>The core functionality in all of those crates lies in their ability to generate
builders for structs<sup id="fnref:bon-core" role="doc-noteref"><a href="#fn:bon-core" class="footnote" rel="footnote">2</a></sup>. If you’ve never used builders, this is best
explained with an example.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(bon::Builder)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">Foo</span><span class="o">&lt;</span><span class="n">S</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">first</span><span class="p">:</span> <span class="n">S</span><span class="p">,</span>
    <span class="n">second</span><span class="p">:</span> <span class="n">T</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="c1">// foo1: Foo&lt;usize,f32&gt;</span>
    <span class="k">let</span> <span class="n">foo1</span> <span class="o">=</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">builder</span><span class="p">()</span>
                <span class="c1">// build order is</span>
                <span class="c1">// arbitrary</span>
                <span class="nf">.second</span><span class="p">(</span><span class="mf">2f32</span><span class="p">)</span>
                <span class="nf">.first</span><span class="p">(</span><span class="mi">1usize</span><span class="p">)</span>
                <span class="nf">.build</span><span class="p">();</span>
                
    <span class="c1">// ⚡ but this will not compile</span>
    <span class="c1">// ⚡ because we have not provided</span>
    <span class="c1">// ⚡ values for all fields</span>
    <span class="c1">// ⭐ this is a deliberate feature</span>
    <span class="k">let</span> <span class="n">foo2</span> <span class="o">=</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">builder</span><span class="p">()</span>
                    <span class="nf">.first</span><span class="p">(</span><span class="mf">1f32</span><span class="p">)</span>
                    <span class="c1">//⚡ forgot second</span>
                    <span class="nf">.build</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This example uses <code class="language-plaintext highlighter-rouge">bon</code>, but it will work –with minimal modifications– with
all the other mentioned crates<sup id="fnref:typed-builder-generics" role="doc-noteref"><a href="#fn:typed-builder-generics" class="footnote" rel="footnote">3</a></sup>. There’s much more that all these crates can do
for you and it’s well worth checking out<sup id="fnref:builder-capabilities" role="doc-noteref"><a href="#fn:builder-capabilities" class="footnote" rel="footnote">4</a></sup>. But now let’s
take a look at something that none of them can do, at least I wasn’t able to make them<sup id="fnref:tried-this" role="doc-noteref"><a href="#fn:tried-this" class="footnote" rel="footnote">5</a></sup>.</p>

<h1 id="problem-statement-lazy-generics">Problem Statement: Lazy Generics</h1>

<p>Let’s first look at piece of code that <em>does not</em> compile using any of the builder
crates mentioned above. Afterwards we’ll discuss what that code might be useful
for. Say we have a runtime boolean condition <code class="language-plaintext highlighter-rouge">cond</code>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// (1)</span>
<span class="k">let</span> <span class="n">builder</span> <span class="o">=</span> <span class="nn">Foo</span><span class="p">::</span><span class="nf">builder</span><span class="p">()</span><span class="nf">.first</span><span class="p">(</span><span class="mf">1.337f32</span><span class="p">);</span>
<span class="k">if</span> <span class="n">cond</span> <span class="p">{</span>
    <span class="c1">// (2)</span>
    <span class="k">let</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">builder</span><span class="nf">.second</span><span class="p">(</span><span class="mi">1usize</span><span class="p">)</span><span class="nf">.build</span><span class="p">();</span>
    <span class="c1">// use foo of type Foo&lt;f32,usize&gt;</span>
    <span class="c1">// ...</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="c1">// (3)</span>
    <span class="c1">// ⚡ this does not compile</span>
    <span class="k">let</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">builder</span><span class="nf">.second</span><span class="p">(</span><span class="o">&amp;</span><span class="s">"hi"</span><span class="p">)</span><span class="nf">.build</span><span class="p">();</span>
    <span class="c1">// use foo of type Foo&lt;f32,&amp;str&gt;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In essence what I try to do is branch the builder on a runtime condition. Both
branches take the same argument for <code class="language-plaintext highlighter-rouge">first</code>, but different arguments for <code class="language-plaintext highlighter-rouge">second</code>.
Not only different arguments, but arguments of different <em>types</em>. This won’t compile
with any of the crates I tested; <a href="https://github.com/geo-ant/builder-experiments/blob/main/src/crosschecks.rs">here</a>
is a link to a repo that shows what I tried.</p>

<p>The reason this doesn’t work is, that the <code class="language-plaintext highlighter-rouge">Foo::builder()</code> function returns a builder
<code class="language-plaintext highlighter-rouge">FooBuilder&lt;S,T,Internal&gt;</code> that has the same <code class="language-plaintext highlighter-rouge">S</code> and <code class="language-plaintext highlighter-rouge">T</code> generic parameters as the generic
type <code class="language-plaintext highlighter-rouge">Foo&lt;S,T&gt;</code> and then some additional internal parameters<sup id="fnref:state-params" role="doc-noteref"><a href="#fn:state-params" class="footnote" rel="footnote">6</a></sup><sup>,</sup><sup id="fnref:typestate-builder" role="doc-noteref"><a href="#fn:typestate-builder" class="footnote" rel="footnote">7</a></sup>. At 
① the type <code class="language-plaintext highlighter-rouge">S</code> of <code class="language-plaintext highlighter-rouge">FooBuilder</code> gets deduced to <code class="language-plaintext highlighter-rouge">f32</code>, which is what we want. However, 
at ② the type <code class="language-plaintext highlighter-rouge">T</code> of <code class="language-plaintext highlighter-rouge">builder</code> gets deduced to <code class="language-plaintext highlighter-rouge">usize</code>. That
fixes the type of <code class="language-plaintext highlighter-rouge">builder</code> to <code class="language-plaintext highlighter-rouge">FooBuilder&lt;f32,usize,...&gt;</code>, and that makes
it a compilation error to pass a <code class="language-plaintext highlighter-rouge">&amp;str</code> in ③. If we had passed a different
value of type <code class="language-plaintext highlighter-rouge">usize</code> that would, of course, have been fine. There are many advantages
that come with this strong type deduction behavior and truth be told, I think
it’s the correct default to have.</p>

<p>But wouldn’t it also be cool if the code above had just worked? In a way, 
I want to avoid this eager evaluation of generics that is the root of my problem
and make the evaluation more <em>lazy</em>. Just because I want to pass a <code class="language-plaintext highlighter-rouge">usize</code> to
<code class="language-plaintext highlighter-rouge">second(...)</code> in ②, doesn’t mean I want to be forced to pass the 
same <em>type</em> in ③. This is what I mean with <em>lazy generics</em>. I feel 
the eager/lazy wording is not quite what I want to express, but I couldn’t
come up with a better term. Maybe <em>decoupled</em> is better? I’ll happily
take suggestions.</p>

<h2 id="so-what">So What?</h2>

<p>If the prospect of learning how builder crates work behind the scenes and doing some
metaprogramming in the process doesn’t already excite you, let me provide
some rationale for why the thing above could indeed be cool. Feel free to skip ahead.
I’ll concede right away that this is a pretty niche use case and that there are
other, saner ways of achieving a very similar effect. Also it is decidedly not my intention
to badmouth the crates above, I hope that much is clear by now.</p>

<p>At work I have a function with a rather nasty signature that takes a lot of 
parameters, some concrete types and some generic ones. It looks something like this:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">calculate</span><span class="o">&lt;</span><span class="n">M</span><span class="p">,</span><span class="n">R</span><span class="o">&gt;</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="o">&amp;</span><span class="p">[</span><span class="nb">f32</span><span class="p">],</span> 
             <span class="n">param</span><span class="p">:</span> <span class="nb">f32</span><span class="p">,</span> 
             <span class="n">mapping</span><span class="p">:</span> <span class="n">M</span><span class="p">,</span> 
             <span class="n">reduction</span><span class="p">:</span> <span class="n">R</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">f32</span> 
    <span class="k">where</span> <span class="n">M</span><span class="p">:</span> <span class="n">Mapping</span><span class="p">,</span>
          <span class="n">R</span><span class="p">:</span> <span class="n">Reduction</span> 
<span class="p">{</span><span class="o">...</span><span class="p">}</span>
</code></pre></div></div>

<p>This is still simplified substantially, but it captures the spirit. The non-generic parameters
are <code class="language-plaintext highlighter-rouge">data</code> and <code class="language-plaintext highlighter-rouge">param</code>. The generic parameters influence the logic which gets executed inside
the function. What I wanted to do, was to use <code class="language-plaintext highlighter-rouge">bon</code> –which allows to construct <a href="https://elastio.github.io/bon/guide/overview#builder-for-a-function">builders
for functions</a>–
to make my callsites more readable. It works by transforming the  functions to structs
with a <code class="language-plaintext highlighter-rouge">.call()</code> method behind the scenes. The <code class="language-plaintext highlighter-rouge">.call()</code> method is like the final
<code class="language-plaintext highlighter-rouge">.build()</code> call in the builder pattern, only that it calls the function with the 
finalized parameters instead of returning a struct instance. Brilliant. 
What I’d like to work is something like this<sup id="fnref:not-bon" role="doc-noteref"><a href="#fn:not-bon" class="footnote" rel="footnote">8</a></sup>:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">calc</span> <span class="o">=</span> <span class="nn">CalculationBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span>
                        <span class="nf">.data</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
                        <span class="nf">.param</span><span class="p">(</span><span class="n">param</span><span class="p">)</span>
                        <span class="nf">.mapping</span><span class="p">(</span><span class="n">mapping</span><span class="p">);</span>

<span class="k">let</span> <span class="n">result</span> <span class="o">=</span> <span class="k">if</span> <span class="n">fiddle</span> <span class="p">{</span> 
                <span class="c1">// fiddling: Fiddling</span>
                <span class="n">calc</span>
                    <span class="nf">.reduction</span><span class="p">(</span><span class="n">fiddling</span><span class="p">)</span>
                    <span class="nf">.call</span><span class="p">()</span>
             <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="c1">// frobnicate: Frobnication</span>
                <span class="n">calc</span>
                    <span class="nf">.reduction</span><span class="p">(</span><span class="n">frobnicate</span><span class="p">)</span>
                    <span class="nf">.call</span><span class="p">();</span>
             <span class="p">};</span>
</code></pre></div></div>

<p>I have run-time conditions that require the function to be called with the same
<code class="language-plaintext highlighter-rouge">data</code>, <code class="language-plaintext highlighter-rouge">param</code>, and <code class="language-plaintext highlighter-rouge">mapping</code> arguments, but with a <code class="language-plaintext highlighter-rouge">reduction</code> of different types.
If I try that, I’ll run into the same problem as above, because the builder enforces
the generics eagerly.</p>

<h1 id="goal-statement-lazy-generics">Goal Statement: Lazy Generics</h1>

<p>Now with all of the introductory stuff out of the way, let’s state the goals
and non-goals for this article:</p>

<ul>
  <li>We want to come up with a builder pattern that supports lazy
generics and explore the complexities that arise from it.</li>
  <li>We won’t go so far to actually write the procedural macro that generates
that builder. Instead we’ll write the builder by hand
and think through the steps that we’d take to automate this.</li>
</ul>

<p>We’ll stick with writing a builder for a struct because that’s the necessary 
first step to writing builders for functions<sup id="fnref:function-builders-where" role="doc-noteref"><a href="#fn:function-builders-where" class="footnote" rel="footnote">9</a></sup>. Meet
our struct:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">Pod</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">S</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">S</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span><span class="p">,</span>
    <span class="n">T</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Debug</span> <span class="o">+</span> <span class="n">MyTrait</span><span class="p">,</span>
<span class="p">{</span>
    <span class="n">field1</span><span class="p">:</span> <span class="nb">f32</span><span class="p">,</span>
    <span class="n">field2</span><span class="p">:</span> <span class="n">S</span><span class="p">,</span>
    <span class="n">field3</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">T</span><span class="p">,</span>
    <span class="n">field4</span><span class="p">:</span> <span class="nn">T</span><span class="p">::</span><span class="n">AssocType</span><span class="p">,</span>
    <span class="n">field5</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="c1">// this is just a bogus</span>
<span class="c1">// trait that gives us </span>
<span class="c1">// associated types</span>
<span class="k">trait</span> <span class="n">MyTrait</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">AssocType</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We’ll write a builder for that structure that allows lazy generics, similar
to what I presented above. There are a couple of things going on in this 
struct, so let’s examine it:</p>

<ul>
  <li>We have 3 generic parameters: the lifetime <code class="language-plaintext highlighter-rouge">'a</code> and the types <code class="language-plaintext highlighter-rouge">S</code> and <code class="language-plaintext highlighter-rouge">T</code>.</li>
  <li>We have a concrete type for <code class="language-plaintext highlighter-rouge">field1</code> thrown in for good measure.</li>
  <li>The generic parameters are constrained in a <code class="language-plaintext highlighter-rouge">where</code> clause<sup id="fnref:constraints" role="doc-noteref"><a href="#fn:constraints" class="footnote" rel="footnote">10</a></sup>.</li>
  <li><code class="language-plaintext highlighter-rouge">field2</code>, <code class="language-plaintext highlighter-rouge">field3</code> and <code class="language-plaintext highlighter-rouge">field5</code> have a <em>direct</em> dependency on one of
the generic type parameters.</li>
  <li><code class="language-plaintext highlighter-rouge">field4</code> <em>indirectly</em> depends on <code class="language-plaintext highlighter-rouge">T</code>, via an associated type.</li>
</ul>

<h2 id="interlude-direct-and-indirect-dependencies-on-types">Interlude: Direct and Indirect Dependencies on Types</h2>

<p>I should explain what I mean with <em>direct</em> and <em>indirect</em> dependencies on 
generic types. People more experienced in type-theory will probably scoff at me for
reinventing terms for things that already exist, but alas, I don’t know type
theory. I’m a mere programmer with a penchant for type system trickery. Do feel
free to leave a comment, though. I’m very happy to learn.</p>

<p>A <em>direct</em> dependency on a generic type <code class="language-plaintext highlighter-rouge">T</code> is when a field type can be deduced
from an assignment to the corresponding field. Let’s make this more concrete
by imagining you had a function that’s generic on <code class="language-plaintext highlighter-rouge">T</code> and takes the field type 
(which has some dependency on type <code class="language-plaintext highlighter-rouge">T</code>) as an argument:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">takes_field</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="cm">/*field type*/</span><span class="p">)</span> <span class="p">{}</span>
</code></pre></div></div>

<p>The question is now: can you write <code class="language-plaintext highlighter-rouge">takes_field(value)</code> without explicitly specifying
the generic type <code class="language-plaintext highlighter-rouge">T</code>? For example, if the field type is <code class="language-plaintext highlighter-rouge">T</code>, <code class="language-plaintext highlighter-rouge">&amp;T</code>, <code class="language-plaintext highlighter-rouge">Option&lt;T&gt;</code>,
<code class="language-plaintext highlighter-rouge">Option&lt;Result&lt;T,()&gt;&gt;</code>, you definitely can! That’s a <em>direct</em> dependency of the
field type on the generic type <code class="language-plaintext highlighter-rouge">T</code>. However, if the field type is something like
<code class="language-plaintext highlighter-rouge">T::AssocType</code>, you can’t. One reason is that many types <code class="language-plaintext highlighter-rouge">T</code> or <code class="language-plaintext highlighter-rouge">U</code> could have
the same <code class="language-plaintext highlighter-rouge">AssocType</code>. Let’s call that an <em>indirect</em> dependency. Those are 
going to be important later.</p>

<h1 id="first-steps">First Steps</h1>

<p>Since we want to have a compile-time verified builder, we must write the builder
as a state machine, where the state is encoded in the type of the builder itself.
This is called the <em>typestate pattern</em> in Rust and I’m going to explain it in this
article. There’s also tons of information about it online. For each field, we want
to encode <em>via the type of the builder</em>, whether it has been set. We have five fields
in our structure, so we give our builder five generic types and five fields.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span><span class="n">F2</span><span class="p">,</span><span class="n">F3</span><span class="p">,</span><span class="n">F4</span><span class="p">,</span><span class="n">F5</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">field1</span><span class="p">:</span> <span class="n">F1</span><span class="p">,</span>
    <span class="n">field2</span><span class="p">:</span> <span class="n">F2</span><span class="p">,</span>
    <span class="n">field3</span><span class="p">:</span> <span class="n">F3</span><span class="p">,</span>
    <span class="n">field4</span><span class="p">:</span> <span class="n">F4</span><span class="p">,</span>
    <span class="n">field5</span><span class="p">:</span> <span class="n">F5</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note that these parameters <code class="language-plaintext highlighter-rouge">F1</code>,…,<code class="language-plaintext highlighter-rouge">F5</code> are totally generic and have no relation
to the actual field types yet. This is essential for avoiding the problems with eager evaluation
of generics mentioned in the introduction. Not even the <code class="language-plaintext highlighter-rouge">field1</code> type is fixed to <code class="language-plaintext highlighter-rouge">f32</code>, although
we know that it can only be <code class="language-plaintext highlighter-rouge">f32</code>. However, we want to use the <em>types</em> to also 
encode whether a field has been set. For that, let’s define a couple of newtypes:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Default)]</span>
<span class="k">struct</span> <span class="n">Empty</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">T</span><span class="p">);</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Empty</code> type indicates that a field has not been set, whereas the
<code class="language-plaintext highlighter-rouge">Assigned&lt;X&gt;</code> type indicates that it was assigned with a value of type <code class="language-plaintext highlighter-rouge">X</code>. Each
generic type of the builder starts at <code class="language-plaintext highlighter-rouge">Empty</code> and transitions to <code class="language-plaintext highlighter-rouge">Assigned&lt;...&gt;</code>
by invoking the corresponding setter function on the builder. Once a field is assigned,
it cannot be assigned again<sup id="fnref:assigned-once" role="doc-noteref"><a href="#fn:assigned-once" class="footnote" rel="footnote">11</a></sup>. That is the basic logic
we’ll implement in the rest of this article, but there are subtleties to consider
as we’ll see. Let’s also define a couple of traits to indicate if a type 
has a value or if it can be assigned still.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">trait</span> <span class="n">Assignable</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">Assignable</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">Empty</span> <span class="p">{}</span>

<span class="k">trait</span> <span class="n">HasValue</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">ValueType</span><span class="p">;</span>
    <span class="k">fn</span> <span class="nf">value</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">::</span><span class="n">ValueType</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">HasValue</span> <span class="k">for</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">ValueType</span> <span class="o">=</span> <span class="n">T</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">value</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">::</span><span class="n">ValueType</span> <span class="p">{</span>
        <span class="k">self</span><span class="na">.0</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Those two traits are not strictly necessary for the code in this article, because they
are only implemented by the <code class="language-plaintext highlighter-rouge">Empty</code> and <code class="language-plaintext highlighter-rouge">Assigned</code> types, respectively.
I’ll mention in this endnote<sup id="fnref:default-values" role="doc-noteref"><a href="#fn:default-values" class="footnote" rel="footnote">12</a></sup> how those traits help us to 
extend the presented method to allow for default values of fields.</p>

<h2 id="start-and-finish">Start and Finish</h2>

<p>Before we implement the state transitions, let’s see where the builder starts
and where it finishes. The builder starts with all fields empty, so we implement
a constructor for exactly that case.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">Empty</span><span class="p">,</span> <span class="n">Empty</span><span class="p">,</span> <span class="n">Empty</span><span class="p">,</span> <span class="n">Empty</span><span class="p">,</span> <span class="n">Empty</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">new</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="k">Self</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span>
            <span class="n">field2</span><span class="p">:</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span>
            <span class="n">field3</span><span class="p">:</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span>
            <span class="n">field4</span><span class="p">:</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span>
            <span class="n">field5</span><span class="p">:</span> <span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This allows us to call <code class="language-plaintext highlighter-rouge">PodBuilder::new()</code> to obtain a builder with all fields
empty. Note, that this builder is not coupled to the generics of the original
<code class="language-plaintext highlighter-rouge">Pod</code> struct. Note further, that this also <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=1fd9cda519eaa0e952692a2dacf9725a">prevents us</a>
from exposing a <code class="language-plaintext highlighter-rouge">Pod::builder()</code> function to obtain the builder, which is the
–very elegant, but in this case also limiting– API that the other crates prefer.</p>

<p>The final stage of the builder is reached when all fields were assigned. Only
then do we allow the user to call the <code class="language-plaintext highlighter-rouge">build()</code> function. This function
consumes the builder and returns an instance of <code class="language-plaintext highlighter-rouge">Pod</code>.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="n">build</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">S</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">Pod</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">S</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">T</span><span class="p">:</span> <span class="n">Debug</span> <span class="o">+</span> <span class="n">MyTrait</span><span class="p">,</span>
        <span class="n">S</span><span class="p">:</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span><span class="p">,</span>
        <span class="n">F1</span><span class="p">:</span> <span class="n">HasValue</span><span class="o">&lt;</span><span class="n">ValueType</span> <span class="o">=</span> <span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">F2</span><span class="p">:</span> <span class="n">HasValue</span><span class="o">&lt;</span><span class="n">ValueType</span> <span class="o">=</span> <span class="n">S</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">F3</span><span class="p">:</span> <span class="n">HasValue</span><span class="o">&lt;</span><span class="n">ValueType</span> <span class="o">=</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">F4</span><span class="p">:</span> <span class="n">HasValue</span><span class="o">&lt;</span><span class="n">ValueType</span> <span class="o">=</span> <span class="nn">T</span><span class="p">::</span><span class="n">AssocType</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">F5</span><span class="p">:</span> <span class="n">HasValue</span><span class="o">&lt;</span><span class="n">ValueType</span> <span class="o">=</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="n">Pod</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="k">self</span><span class="py">.field1</span><span class="nf">.value</span><span class="p">(),</span>
            <span class="n">field2</span><span class="p">:</span> <span class="k">self</span><span class="py">.field2</span><span class="nf">.value</span><span class="p">(),</span>
            <span class="n">field3</span><span class="p">:</span> <span class="k">self</span><span class="py">.field3</span><span class="nf">.value</span><span class="p">(),</span>
            <span class="n">field4</span><span class="p">:</span> <span class="k">self</span><span class="py">.field4</span><span class="nf">.value</span><span class="p">(),</span>
            <span class="n">field5</span><span class="p">:</span> <span class="k">self</span><span class="py">.field5</span><span class="nf">.value</span><span class="p">(),</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Note, that the <code class="language-plaintext highlighter-rouge">build</code> function <em>does</em> include the generic types <code class="language-plaintext highlighter-rouge">'a</code>, <code class="language-plaintext highlighter-rouge">S</code>, and <code class="language-plaintext highlighter-rouge">T</code>
of <code class="language-plaintext highlighter-rouge">Pod</code>, that we had avoided before. It also includes all the trait bounds on
<code class="language-plaintext highlighter-rouge">S</code> and <code class="language-plaintext highlighter-rouge">T</code>. It allows us to call <code class="language-plaintext highlighter-rouge">builder.build()</code> on an instance of <code class="language-plaintext highlighter-rouge">PodBuilder</code>
if and only if it indicates –via its type signature– that all fields have been set.
Now let’s see how we actually implement state transitions to get us from
an all-empty builder to its final state one by one.</p>

<h2 id="setting-field-1-state-transitions-for-concrete-types">Setting Field 1: State Transitions for Concrete Types</h2>

<p><code class="language-plaintext highlighter-rouge">Pod::field1</code> is a concrete type, in this case <code class="language-plaintext highlighter-rouge">f32</code>. This is the simplest case
we can encounter when implementing state transitions. Whether <code class="language-plaintext highlighter-rouge">field1</code> is set in our
builder is encoded via the corresponding type <code class="language-plaintext highlighter-rouge">F1</code>. If this type implements the
<code class="language-plaintext highlighter-rouge">Assignable&lt;f32&gt;</code> trait (which in our case is just a more complicated way of 
saying that <code class="language-plaintext highlighter-rouge">F1</code> is <code class="language-plaintext highlighter-rouge">Empty</code>), then we want to consume our builder,
set the field, and return a new builder that encodes in its type that <code class="language-plaintext highlighter-rouge">field1</code>
was set.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">field1</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">field1</span><span class="p">:</span> <span class="nb">f32</span><span class="p">)</span> 
       <span class="k">-&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">Assigned</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">F1</span><span class="p">:</span> <span class="n">Assignable</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="n">PodBuilder</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="nf">Assigned</span><span class="p">(</span><span class="n">field1</span><span class="p">),</span>
            <span class="n">field2</span><span class="p">:</span> <span class="k">self</span><span class="py">.field2</span><span class="p">,</span>
            <span class="n">field3</span><span class="p">:</span> <span class="k">self</span><span class="py">.field3</span><span class="p">,</span>
            <span class="n">field4</span><span class="p">:</span> <span class="k">self</span><span class="py">.field4</span><span class="p">,</span>
            <span class="n">field5</span><span class="p">:</span> <span class="k">self</span><span class="py">.field5</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the type signature of the builder returned from this setter, <code class="language-plaintext highlighter-rouge">F1</code> is now of type <code class="language-plaintext highlighter-rouge">Assigned&lt;f32&gt;</code>
and carries the assigned value in <code class="language-plaintext highlighter-rouge">field1</code>. All other types <code class="language-plaintext highlighter-rouge">F2</code>,…,<code class="language-plaintext highlighter-rouge">F5</code>
just stay as they were and their values get moved into the new instance of
the builder. That allows us to call the setter function before or after any
of the other setter functions, the order of initialization does not matter.</p>

<h2 id="setting-field-2-simple-state-transitions-for-generic-types">Setting Field 2: Simple State Transitions for Generic Types</h2>

<p>Setting <code class="language-plaintext highlighter-rouge">field2</code> is almost as simple as setting <code class="language-plaintext highlighter-rouge">field1</code>, let’s see how its
done:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="n">field2</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">field2</span><span class="p">:</span> <span class="n">S</span><span class="p">)</span> 
       <span class="k">-&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">S</span><span class="p">:</span> <span class="n">Display</span><span class="p">,</span>
        <span class="n">F2</span><span class="p">:</span> <span class="n">Assignable</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="n">PodBuilder</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="k">self</span><span class="py">.field1</span><span class="p">,</span>
            <span class="n">field2</span><span class="p">:</span> <span class="nf">Assigned</span><span class="p">(</span><span class="n">field2</span><span class="p">),</span>
            <span class="n">field3</span><span class="p">:</span> <span class="k">self</span><span class="py">.field3</span><span class="p">,</span>
            <span class="n">field4</span><span class="p">:</span> <span class="k">self</span><span class="py">.field4</span><span class="p">,</span>
            <span class="n">field5</span><span class="p">:</span> <span class="k">self</span><span class="py">.field5</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The only thing that changed compared to above, is that our setter function
now accepts a generic type <code class="language-plaintext highlighter-rouge">S</code> and that we restrict <code class="language-plaintext highlighter-rouge">S</code> in a <code class="language-plaintext highlighter-rouge">where</code> clause
with the same restrictions as in the original <code class="language-plaintext highlighter-rouge">Pod</code> type. It’s really that
simple, but as we’ll see below it’s not always going to be this simple when
generic types are involved. It works like this here because of these
reasons:</p>

<ol>
  <li>the type of <code class="language-plaintext highlighter-rouge">Pod::field2</code> has a <em>direct</em> dependency on <code class="language-plaintext highlighter-rouge">S</code>. That means the type can be
deduced in the setter function <em>and</em></li>
  <li>the <code class="language-plaintext highlighter-rouge">where</code> clause restricting <code class="language-plaintext highlighter-rouge">S</code> has no dependencies on other types</li>
</ol>

<p>This gives us a taste of the complexities that we’ll be faced with in the
next sections, so let’s enjoy for a moment that it really can be this simple
sometimes before we move on.</p>

<h2 id="setting-fields-3-and-5-coupled-generic-types">Setting Fields 3 and 5: Coupled Generic Types</h2>

<p>Both the types of <code class="language-plaintext highlighter-rouge">Pod::field3</code> (which is <code class="language-plaintext highlighter-rouge">&amp;'a T</code>) and the type of <code class="language-plaintext highlighter-rouge">Pod::field5</code>
(which is <code class="language-plaintext highlighter-rouge">Option&lt;T&gt;</code>) have a <em>direct</em> dependency on <code class="language-plaintext highlighter-rouge">T</code>, which means <code class="language-plaintext highlighter-rouge">T</code> can be
deduced by assigning to either of these types. When I initially drafted this section
and the corresponding code, I had a whole spiel about how it was important to
check whether <code class="language-plaintext highlighter-rouge">field3</code> was already set when setting <code class="language-plaintext highlighter-rouge">field5</code> (and vice versa), to
help the type deduction. I’ve archived that code 
<a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=ff4392d0e60e2c65e0d89e06dc946ad2">on the playground</a>,
but it was completely overblown. It turns out, if the field types
obey the conditions mentioned above, we can just treat them like we treated the
previous field. The correspoding setter implementations become:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="n">field3</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">field3</span><span class="p">:</span> <span class="o">&amp;</span><span class="nv">'a</span> <span class="n">T</span><span class="p">)</span> 
       <span class="k">-&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span>
    <span class="k">where</span>
        <span class="n">T</span><span class="p">:</span> <span class="n">Debug</span> <span class="o">+</span> <span class="n">MyTrait</span><span class="p">,</span>
        <span class="n">F3</span><span class="p">:</span> <span class="n">Assignable</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="n">PodBuilder</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="k">self</span><span class="py">.field1</span><span class="p">,</span>
            <span class="n">field2</span><span class="p">:</span> <span class="k">self</span><span class="py">.field2</span><span class="p">,</span>
            <span class="n">field3</span><span class="p">:</span> <span class="nf">Assigned</span><span class="p">(</span><span class="n">field3</span><span class="p">),</span>
            <span class="n">field4</span><span class="p">:</span> <span class="k">self</span><span class="py">.field4</span><span class="p">,</span>
            <span class="n">field5</span><span class="p">:</span> <span class="k">self</span><span class="py">.field5</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="n">field5</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">field5</span><span class="p">:</span> <span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">)</span> 
       <span class="k">-&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F3</span><span class="p">,</span> <span class="n">F4</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;&gt;</span>
    <span class="k">where</span>
        <span class="n">T</span><span class="p">:</span> <span class="n">Debug</span> <span class="o">+</span> <span class="n">MyTrait</span><span class="p">,</span>
        <span class="n">F5</span><span class="p">:</span> <span class="n">Assignable</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="p">{</span>
        <span class="n">PodBuilder</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="k">self</span><span class="py">.field1</span><span class="p">,</span>
            <span class="n">field2</span><span class="p">:</span> <span class="k">self</span><span class="py">.field2</span><span class="p">,</span>
            <span class="n">field3</span><span class="p">:</span> <span class="k">self</span><span class="py">.field3</span><span class="p">,</span>
            <span class="n">field4</span><span class="p">:</span> <span class="k">self</span><span class="py">.field4</span><span class="p">,</span>
            <span class="n">field5</span><span class="p">:</span> <span class="nf">Assigned</span><span class="p">(</span><span class="n">field5</span><span class="p">),</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>It’s just the same as in the section above, there’s not much more to it than that.
 A note on type deduction: the final call to <code class="language-plaintext highlighter-rouge">build()</code> forces that <code class="language-plaintext highlighter-rouge">T</code> is deduced
as one unified type. So, as long as we end our builder
chains –including the ones that are forked between branches– with a call to
<code class="language-plaintext highlighter-rouge">build()</code>, the type system is smart enough to conclude that the assigned types
are supposed to be the same.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">builder</span> <span class="o">=</span> <span class="nn">PodBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span>
    <span class="nf">.field5</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="s">"hi"</span><span class="nf">.into</span><span class="p">()));</span>
    <span class="nf">.field3</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">())</span>
<span class="c1">// -- other field assignments, possibly branches --</span>
<span class="c1">// let builder = ...</span>

<span class="c1">// assuming this is now a finished builder</span>
<span class="k">let</span> <span class="n">foo</span> <span class="o">=</span> <span class="n">builder</span><span class="nf">.build</span><span class="p">();</span>

</code></pre></div></div>

<p>We’ll have all the sweet type deduction we come to expect. The important thing
is that the builder chains gets finished with the <code class="language-plaintext highlighter-rouge">build()</code> method. However, that’s
guaranteed to happen, because why else would you have a builder in the first place…?</p>

<h2 id="setting-field-4-field-types-with-indirect-dependencies-on-generic-types">Setting Field 4: Field Types with Indirect Dependencies on Generic Types</h2>

<p>The type of <code class="language-plaintext highlighter-rouge">Pod::field4</code> is <code class="language-plaintext highlighter-rouge">T::AssocType</code>, which means it depends on the
generic type <code class="language-plaintext highlighter-rouge">T</code> indirectly. We can’t deduce <code class="language-plaintext highlighter-rouge">T</code> from assigning to <code class="language-plaintext highlighter-rouge">field4</code>.
Somehow, we have to know the type <code class="language-plaintext highlighter-rouge">T</code>. We could force the user to explicitly
tell us, but I don’t like that so much. We could also expose
the setter for <code class="language-plaintext highlighter-rouge">field4</code> only if we know <code class="language-plaintext highlighter-rouge">T</code> already, which is the option
that I went with. That is the case when <code class="language-plaintext highlighter-rouge">field3</code> or
<code class="language-plaintext highlighter-rouge">field5</code> (or both) have already been assigned.</p>

<h3 id="excursion-dependency-graphs">Excursion: Dependency Graphs</h3>

<p>Since the ultimate goal (not in this article, but eventually) is to implement this
into a derive macro, let’s go through this analysis a little more formally. Let’s
visualize the direct and indirect dependencies of the builder types (lower row)
and the structure generic types (upper row).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Pod:              S    'a     T
                  |    |     /|\
                  |    |    / | \
                  d    d   d  i  d    
                  |    |  /   |   \
                  |    | /    |    \
Builder:   F1     F2   F3     F4   F5
</code></pre></div></div>

<p>The generic types of the builder <code class="language-plaintext highlighter-rouge">F1</code>,…,<code class="language-plaintext highlighter-rouge">F5</code> have a one-to-one correspondence
with the builder fields <code class="language-plaintext highlighter-rouge">PodBuilder::field1</code>,…<code class="language-plaintext highlighter-rouge">Podbuilder::field5</code>.
Their dependency on the generic types <code class="language-plaintext highlighter-rouge">'a</code>, <code class="language-plaintext highlighter-rouge">S</code>, and <code class="language-plaintext highlighter-rouge">T</code> is via the field types
<code class="language-plaintext highlighter-rouge">Pod::field1</code>,…,<code class="language-plaintext highlighter-rouge">Pod::field5</code> of the original structure. Here, a line with an
<code class="language-plaintext highlighter-rouge">i</code> is an <em>indirect</em> dependency and a line with a <code class="language-plaintext highlighter-rouge">d</code> is a <em>direct</em> dependency.
Here we have a pretty simple (not fully connected) dependency graph. We are
trying to set <code class="language-plaintext highlighter-rouge">field4</code>, which is associated with type <code class="language-plaintext highlighter-rouge">F4</code>. So we look at the graph
and collect all the original generic types on whom <code class="language-plaintext highlighter-rouge">F4</code> depends <em>indirectly</em>. Here, 
that’s just <code class="language-plaintext highlighter-rouge">T</code>, which only has other <em>direct</em> dependencies. So we go
ahead and collect those, in our case
<code class="language-plaintext highlighter-rouge">F3</code> and <code class="language-plaintext highlighter-rouge">F5</code>. Now we know that it’s a prerequisite that either one (or both)
of the generic arguments <code class="language-plaintext highlighter-rouge">F3</code>, <code class="language-plaintext highlighter-rouge">F5</code> have their corresponding value assigned, before
we can assign to the <code class="language-plaintext highlighter-rouge">field4</code> associated with <code class="language-plaintext highlighter-rouge">F4</code>.</p>

<h3 id="implementing-setters-that-require-other-fields-to-be-set">Implementing Setters that Require Other Fields to be Set</h3>

<p>Let’s now see how to implement the setter for <code class="language-plaintext highlighter-rouge">field4</code> only if <code class="language-plaintext highlighter-rouge">F3</code> and/or <code class="language-plaintext highlighter-rouge">F5</code>
were set. There are three combinations for (<code class="language-plaintext highlighter-rouge">F3</code>,<code class="language-plaintext highlighter-rouge">F5</code>) where we want to allow the
setter to be invoked, which are (Empty, Assigned), (Assigned, Empty), and (Assigned, Assigned). In
a procedural macro, I’d probably output dedicated code for all of those 
combinatorial cases. However, for this particular application we can see that
we can collapse (for example) the last two cases into a single <code class="language-plaintext highlighter-rouge">impl</code> block.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// covers the case (F3,F5) = (Empty,Assigned)</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="o">&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">Empty</span><span class="p">,</span> <span class="n">Empty</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;&gt;</span>
<span class="k">where</span>
    <span class="n">T</span><span class="p">:</span> <span class="n">Debug</span> <span class="o">+</span> <span class="n">MyTrait</span><span class="p">,</span>
<span class="p">{</span>
    <span class="k">fn</span> <span class="nf">field4</span><span class="p">(</span>
        <span class="k">self</span><span class="p">,</span>
        <span class="n">field4</span><span class="p">:</span> <span class="nn">T</span><span class="p">::</span><span class="n">AssocType</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">Empty</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="nn">T</span><span class="p">::</span><span class="n">AssocType</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="nb">Option</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;&gt;</span> <span class="p">{</span>
        <span class="n">PodBuilder</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="k">self</span><span class="py">.field1</span><span class="p">,</span>
            <span class="n">field2</span><span class="p">:</span> <span class="k">self</span><span class="py">.field2</span><span class="p">,</span>
            <span class="n">field3</span><span class="p">:</span> <span class="n">Empty</span><span class="p">,</span>
            <span class="n">field4</span><span class="p">:</span> <span class="nf">Assigned</span><span class="p">(</span><span class="n">field4</span><span class="p">),</span>
            <span class="n">field5</span><span class="p">:</span> <span class="k">self</span><span class="py">.field5</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// covers the cases (F3,F5) = (Assigned, Empty) or (Assigned, Assigned)</span>
<span class="k">impl</span><span class="o">&lt;</span><span class="nv">'a</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">Empty</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">T</span><span class="p">:</span> <span class="n">Debug</span> <span class="o">+</span> <span class="n">MyTrait</span><span class="p">,</span>
<span class="p">{</span>
    <span class="k">fn</span> <span class="nf">field4</span><span class="p">(</span>
        <span class="k">self</span><span class="p">,</span>
        <span class="n">field4</span><span class="p">:</span> <span class="nn">T</span><span class="p">::</span><span class="n">AssocType</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="n">PodBuilder</span><span class="o">&lt;</span><span class="n">F1</span><span class="p">,</span> <span class="n">F2</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;&amp;</span><span class="nv">'a</span> <span class="n">T</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">Assigned</span><span class="o">&lt;</span><span class="nn">T</span><span class="p">::</span><span class="n">AssocType</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">F5</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="n">PodBuilder</span> <span class="p">{</span>
            <span class="n">field1</span><span class="p">:</span> <span class="k">self</span><span class="py">.field1</span><span class="p">,</span>
            <span class="n">field2</span><span class="p">:</span> <span class="k">self</span><span class="py">.field2</span><span class="p">,</span>
            <span class="n">field3</span><span class="p">:</span> <span class="k">self</span><span class="py">.field3</span><span class="p">,</span>
            <span class="n">field4</span><span class="p">:</span> <span class="nf">Assigned</span><span class="p">(</span><span class="n">field4</span><span class="p">),</span>
            <span class="n">field5</span><span class="p">:</span> <span class="k">self</span><span class="py">.field5</span><span class="p">,</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And just like that, we have finished our implementation of the builder. Let’s see
what we can do with it.</p>

<h1 id="using-the-builder-with-lazy-generics">Using the Builder with Lazy Generics</h1>

<p>I’ve put <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=579320ddbe4c8cfd95f4cdbb95b431dc">the code on the playground</a>,
so please do play with it. Of course, the builder supports the most basic
application, which is constructing a <code class="language-plaintext highlighter-rouge">Pod</code> from one complete chain:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">pod</span> <span class="o">=</span> <span class="nn">PodBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span>
    <span class="nf">.field2</span><span class="p">(</span><span class="mf">1f64</span><span class="p">)</span>
    <span class="nf">.field1</span><span class="p">(</span><span class="mf">337.</span><span class="p">)</span>
    <span class="nf">.field5</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="s">"hi"</span><span class="nf">.into</span><span class="p">()))</span>
    <span class="nf">.field3</span><span class="p">(</span><span class="o">&amp;</span><span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">())</span>
    <span class="nf">.field4</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
    <span class="nf">.build</span><span class="p">();</span>
</code></pre></div></div>

<p>But that’s what all the other builder crates can do as well, so let’s look
at some lazy generics:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">MyTrait</span> <span class="k">for</span> <span class="nb">i32</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">AssocType</span> <span class="o">=</span> <span class="nb">f32</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">MyTrait</span> <span class="k">for</span> <span class="nb">String</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">AssocType</span> <span class="o">=</span> <span class="nb">usize</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="n">cond</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span> <span class="n">othercond</span><span class="p">:</span> <span class="nb">bool</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">s</span> <span class="o">=</span> <span class="nn">String</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">builder</span> <span class="o">=</span> <span class="nn">PodBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span><span class="nf">.field1</span><span class="p">(</span><span class="mf">1.</span><span class="p">);</span>
    <span class="k">if</span> <span class="n">cond</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">builder</span> <span class="o">=</span> <span class="n">builder</span>
            <span class="nf">.field2</span><span class="p">(</span><span class="s">"foo"</span><span class="p">);</span>
        <span class="k">if</span> <span class="n">othercond</span> <span class="p">{</span>
            <span class="c1">// pod: Pod&lt;&amp;str,String&gt;</span>
            <span class="k">let</span> <span class="n">pod</span> <span class="o">=</span> <span class="n">builder</span>
                <span class="nf">.field5</span><span class="p">(</span><span class="nb">None</span><span class="p">)</span>
                <span class="nf">.field4</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
                <span class="nf">.field3</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">)</span>
                <span class="nf">.build</span><span class="p">();</span>
            <span class="c1">// use pod...</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="c1">// pod: Pod&lt;&amp;str,i32&gt;</span>
            <span class="k">let</span> <span class="n">pod</span> <span class="o">=</span> <span class="n">builder</span>
                <span class="nf">.field5</span><span class="p">(</span><span class="nb">None</span><span class="p">)</span><span class="err">.</span>
                <span class="nf">.field4</span><span class="p">(</span><span class="mf">1.</span><span class="p">)</span>
                <span class="nf">.field3</span><span class="p">(</span><span class="o">&amp;</span><span class="mi">1</span><span class="p">)</span>
                <span class="nf">.build</span><span class="p">();</span>
            <span class="c1">// use pod...</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="c1">// pod: Pod&lt;i32, String&gt;</span>
        <span class="k">let</span> <span class="n">pod</span> <span class="o">=</span> <span class="n">builder</span>
            <span class="nf">.field3</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="p">)</span>
            <span class="nf">.field5</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="s">"hi"</span><span class="nf">.into</span><span class="p">()))</span>
            <span class="nf">.field2</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
            <span class="nf">.field4</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
            <span class="nf">.build</span><span class="p">();</span>
        <span class="c1">// use pod...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It shows that we can fork the builder based on runtime conditions without
locking in unassigned types across forks. The builder becomes a stemcell with
lazy generics.</p>

<h1 id="outlook-function-builders-and-where-clauses">Outlook: Function Builders and Where Clauses</h1>

<p>This has been a long and complicated article, but allow me some final remarks. I think
the most interesting application of builders with lazy generics, is the possibility
of using them to create builders for generic functions.
In principle, there’s nothing stopping us from doing that now, based on what
we did. There are two problems that we’ll run into:</p>

<ol>
  <li>Generic types which are not part of the function arguments or not
deducable via the function argument types.</li>
  <li>More complicated trait bounds, such as the circular bounds <code class="language-plaintext highlighter-rouge">where T: Add&lt;S&gt;, S: Sub&lt;T&gt;</code>.</li>
</ol>

<p>We can encounter both problems<sup id="fnref:phantom-data" role="doc-noteref"><a href="#fn:phantom-data" class="footnote" rel="footnote">13</a></sup> in structs as well. I think the first
one is easy to solve, since we can just have our builder’s constructor force the user to specify those
types that cannot be deduced. The second problem is a bit trickier:</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">do_something</span><span class="p">(</span><span class="n">first</span><span class="p">:</span> <span class="n">S</span><span class="p">,</span> <span class="n">second</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">bool</span> 
    <span class="k">where</span> <span class="n">S</span><span class="p">:</span> <span class="nb">Sub</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="o">+</span> <span class="nb">Clone</span><span class="p">,</span>
          <span class="n">T</span><span class="p">:</span> <span class="nb">Add</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span> <span class="o">+</span> <span class="nb">Ord</span><span class="p">,</span>
          
<span class="p">{</span><span class="o">...</span><span class="p">}</span>
</code></pre></div></div>

<p>In the setters above, we just copied all the trait bounds of the particular generic type.
That won’t work anymore because we can’t name <code class="language-plaintext highlighter-rouge">T</code> in the setter for <code class="language-plaintext highlighter-rouge">S</code>,
except if we require the type <code class="language-plaintext highlighter-rouge">T</code> to be known. However, we’d then 
also have to require the type <code class="language-plaintext highlighter-rouge">S</code> to be known in our setter for <code class="language-plaintext highlighter-rouge">T</code>. 
We could then never actually set <em>any</em> of these fields due to the circular dependency.
But I <em>think</em> the solution is easier than it looks at first: we should just be
able to skip all the restrictions that depend on generic types other the ones
named in the field type associated with the setter. That means in the setter for <code class="language-plaintext highlighter-rouge">first</code>
we just restrict <code class="language-plaintext highlighter-rouge">S</code> on <code class="language-plaintext highlighter-rouge">Clone</code> and in the setter for <code class="language-plaintext highlighter-rouge">second</code> we restrict
<code class="language-plaintext highlighter-rouge">T</code> only on <code class="language-plaintext highlighter-rouge">Ord</code>. In the final <code class="language-plaintext highlighter-rouge">build()</code> call, we can then enforce all the
restrictions again, because we can name all of the types.</p>

<h1 id="final-words">Final Words</h1>

<p>If you read this far, cheers to you. There are more things that I’d like to say
about this method, but this post is already very long. So I’ll just leave it at
that. I don’t claim that all builders need to support the lazy generics as presented
here. I just hadn’t seen it anywhere else, maybe it’s out there and I did not
look hard enough. I’m very happy to engage in the comments at the end of this
article.</p>

<h1 id="endnotes">Endnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:runtime-builders" role="doc-endnote">
      <p>There’s also the excellent and venerable <a href="https://crates.io/crates/derive_builder"><code class="language-plaintext highlighter-rouge">derive_builder</code></a> (ver <code class="language-plaintext highlighter-rouge">0.20.1</code>), which does all validations (including forgotten fields) at run time. The problems with generics addressed in this article also apply to this crate. <a href="#fnref:runtime-builders" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:bon-core" role="doc-endnote">
      <p>The <code class="language-plaintext highlighter-rouge">bon</code> crate allows to create builders not only for structs but also e.g. for functions. However, this functionality internally transforms the function into a structure with a <code class="language-plaintext highlighter-rouge">.call()</code> method. The function arguments are made into fields of the generated structure. Then, a builder for this new struct is generated and thus it has the same limitations as the builders that are applied directly to structures. <a href="#fnref:bon-core" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:typed-builder-generics" role="doc-endnote">
      <p>Except the <code class="language-plaintext highlighter-rouge">typestate_builder</code> crate which, at the time of writing, still struggles with generics. At least when we use the intended API. It can be hacked to make this and the simple lazy generic example work, see <a href="https://github.com/geo-ant/builder-experiments/blob/main/src/crosschecks.rs">here</a> <a href="#fnref:typed-builder-generics" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:builder-capabilities" role="doc-endnote">
      <p>Examples including: optional fields, <code class="language-plaintext highlighter-rouge">Into</code>-conversions, default parameters and much more, depending on the crate. <a href="https://elastio.github.io/bon/guide/alternatives">Here</a> is a great overview by the <code class="language-plaintext highlighter-rouge">bon</code> maintainers. <a href="#fnref:builder-capabilities" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:tried-this" role="doc-endnote">
      <p>I’ve of course tried the code in question with all the mentioned crates and verified that it fails to compile. I’ve also looked at the macro expansion, which makes me pretty confident that this is not something that these crates <em>can</em> do. However, I might still have gotten things wrong or missed additional options and I am happy to be corrected. See <a href="https://github.com/geo-ant/builder-experiments/blob/main/src/crosschecks.rs">here</a> to see what I did. <a href="#fnref:tried-this" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:state-params" role="doc-endnote">
      <p>The exact form of the other generic parameters varies slightly between crates, but the principle is always the same. Again, bear with me… we’ll go into the details later. <a href="#fnref:state-params" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:typestate-builder" role="doc-endnote">
      <p>This is where the <code class="language-plaintext highlighter-rouge">typestate_builder</code> crate does something different. It still runs into the trap of exposing the <code class="language-plaintext highlighter-rouge">Foo::builder()</code> function as the only way to expose a builder. That is the reason why this example will fail. But it can be tricked into providing an empty builder and then we can actually make this example compile. However, this crate is currently not able to work with the more complex example that I’ll present in the sections below. <a href="#fnref:typestate-builder" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:not-bon" role="doc-endnote">
      <p>This is less elegant than <a href="https://elastio.github.io/bon/guide/overview#builder-for-a-function">how it would actually look</a> in <code class="language-plaintext highlighter-rouge">bon</code>. But for the sake of this article, it’s better like this, since this looks more similar to the builder pattern in the intro section. <a href="#fnref:not-bon" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:function-builders-where" role="doc-endnote">
      <p>Function builders don’t necessarily follow trivially, because <code class="language-plaintext highlighter-rouge">where</code> clauses for generics in functions typically involve constraints that are not essential to construct the arguments, but to perform the logic of the function (think <code class="language-plaintext highlighter-rouge">T: Add&lt;U::AssocType&gt;</code>). In this case we’d like a way to specify two where clauses: one with the essential requirements that enable us to construct the argument types without compilation errors. This where clause would go on the builder struct. And an additional where clause for all the logic requirements that the function needs. This (plus the essential where clause) would then be stuck on the <code class="language-plaintext highlighter-rouge">.call()</code> method. We could achieve this by allowing attribute macros that capture the essential <code class="language-plaintext highlighter-rouge">where</code> clause, like <code class="language-plaintext highlighter-rouge">#[essential(where T: Scalar)]</code> and then the stuff that is not essential for constructing the types could go into the actual <code class="language-plaintext highlighter-rouge">where</code> clause of the function. Our procedural macro would then take care of splitting and combining the where clauses as necessary. <a href="#fnref:function-builders-where" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:constraints" role="doc-endnote">
      <p>When writing procedural macros, it’s important to keep in mind that <code class="language-plaintext highlighter-rouge">where</code> clauses are not the only places where constraints can be places on parameters. To gather all the constraints one has to parse the generic types in the struct definition as well. <a href="#fnref:constraints" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:assigned-once" role="doc-endnote">
      <p>This is a choice on my part, but it’s something that the other builders to as well. We could also modify the state machine pattern to allow overwriting, but I feel it’s more likely an indication of an accident on the user’s part. <a href="#fnref:assigned-once" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:default-values" role="doc-endnote">
      <p>At least for non-generic field types it’s easy to allow for default values (it gets more complicated if the field type is generic). What we do is introduce another type <code class="language-plaintext highlighter-rouge">WithDefault&lt;T&gt;(T)</code> that implements <code class="language-plaintext highlighter-rouge">Assignable&lt;T&gt;</code>, but also <code class="language-plaintext highlighter-rouge">HasValue&lt;ValueType = T&gt;</code>. For fields where we want to allow default values in case the user does not set one, we start with a <code class="language-plaintext highlighter-rouge">WithDefault&lt;T&gt;</code> containing the default value rather than <code class="language-plaintext highlighter-rouge">Empty</code>. If we assign to a <code class="language-plaintext highlighter-rouge">WithDefault</code>, then it transitions to <code class="language-plaintext highlighter-rouge">Assigned</code>. But even if we don’t, the builder will allow to call <code class="language-plaintext highlighter-rouge">build</code> because <code class="language-plaintext highlighter-rouge">WithDefault</code> also implements <code class="language-plaintext highlighter-rouge">HasValue</code>. <a href="#fnref:default-values" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:phantom-data" role="doc-endnote">
      <p>In structs we can’t have types that aren’t part of the fields at all. We’ll at least have phantom fields, but in practice those should not expose setters, but instead force the user to specify the types explicitly. <a href="#fnref:phantom-data" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="metaprogramming" /><category term="generics" /><category term="design-patterns" /><summary type="html"><![CDATA[While using compile-time builder generator crates, I realized that I had run into a niche problem that required lot more flexibility with generic structs and functions than I was getting. If you like, follow me down a rabbit hole and explore the builder pattern from a very generic-centric perspective.]]></summary></entry><entry><title type="html">The Covariance Matrix for Variable Projection with Multiple Right Hand Sides</title><link href="https://geo-ant.github.io/blog/2024/covariance-varpro-mrhs/" rel="alternate" type="text/html" title="The Covariance Matrix for Variable Projection with Multiple Right Hand Sides" /><published>2024-09-30T00:00:00+00:00</published><updated>2024-09-30T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2024/covariance-varpro-mrhs</id><content type="html" xml:base="https://geo-ant.github.io/blog/2024/covariance-varpro-mrhs/"><![CDATA[<p>In this article, I explore how to efficiently calculate the covariance matrix
of the best fit parameters for global fitting problems that use the
variable projection (VarPro) algorithm. It’s a very niche topic, but I do need it for
my open source library so I might as well write it down. It might be helpful
for people looking to gain a deeper understanding of the math behind VarPro.</p>

<h1 id="varpro-minimization-recap">VarPro Minimization: Recap</h1>

<p>In the <a href="/blog/2024/variable-projection-part-2-multiple-right-hand-sides/">previous article</a>
in our VarPro series on this blog, we saw that we can express Variable Projection
with multiple right hand sides using a couple of equivalent approaches. We can,
for example, either use a more matrix oriented approach or a more
vector oriented approach to the same effect. But this time, it is 
the vector oriented approach that makes it a bit easier for me to tackle the 
problem at hand.</p>

<p>I’ll very briefly restate the fundamentals of global fitting multiple right hand sides.
If you haven’t read the <a href="/blog/2024/variable-projection-part-2-multiple-right-hand-sides/">previous article</a>,
I highly suggest you do. Fitting multiple right hand sides means that we have
\(N_s\) <em>vectors</em> \(\boldsymbol{y}_1,\dots,\boldsymbol{y}_{N_s}\in \mathbb{R}^{N_y}\), that we
want to fit with vector valued functions 
\(\boldsymbol{f}_1,\dots,\boldsymbol{f}_{N_s} \in \mathbb{R}^{N_y}\),
respectively. Each function can be written in matrix form as a linear combination
of nonlinear base functions like so:</p>

\[\boldsymbol{f}_k(\boldsymbol{\alpha},\boldsymbol{c}_k) = \boldsymbol{\Phi}(\boldsymbol{\alpha})\, \boldsymbol{c}_k, \label{f-phi}\tag{1}\]

<p>where \(\boldsymbol{\Phi}(\boldsymbol{\alpha}) \in \mathbb{R}^{N_y \times N_c}\)
is the matrix of the \(N_c\) nonlinear basis functions and \(\boldsymbol{c}_k \in \mathbb{R}^{N_c}\)
are the <em>linear coefficients</em>, which can vary with \(j\), and \(\boldsymbol{\alpha} \in \mathbb{R}^{N_\alpha}\)
are the <em>nonlinear parameters</em> of the problem, which are shared across all \(k\).
This latter aspect is where the term <em>global fitting</em> comes from. Now, let’s
start writing the least squares fitting problem into vector form. We begin by
introducing a global parameter vector \(\boldsymbol{p}\) that bundles all the linear and
nonlinear parameters for the global fitting problem like so:</p>

\[\boldsymbol{p}=\left[
\begin{matrix}
\boldsymbol{c}_1 \\
\vdots \\
\boldsymbol{c}_{N_s} \\
\boldsymbol{\alpha}
\end{matrix}
\right] \in \mathbb{R}^{N_s\cdot N_c + N_\alpha} \label{p-def}\tag{2}\]

<p>Then, we write the weighted residual of the \(k\)-th dataset as</p>

\[\boldsymbol{r}_{w,k}(\boldsymbol{p}) = \boldsymbol{W} (\boldsymbol{y}_k - \boldsymbol{f}_k(\boldsymbol{p})), \label{rwk-def}\tag{3}\]

<p>where the weight matrix \(\boldsymbol{W}\in \mathbb{R}^{N_y\times N_y}\) is
shared across all \(k\). Now, we introduce three more stacked vectors:
for the dataset, for the function values, and for the weighted residuals,
respectively:</p>

\[\boldsymbol{y}:=\left[
\begin{matrix}
\boldsymbol{y}_1 \\
\vdots \\
\boldsymbol{y}_{N_s} \\
\end{matrix}
\right] ,\;
\boldsymbol{f}(\boldsymbol{p}):=\left[
\begin{matrix}
\boldsymbol{f}_1 \\
\vdots \\
\boldsymbol{f}_{N_s} \\
\end{matrix}
\right] ,\;
\boldsymbol{r}_w(\boldsymbol{p}):=\left[
\begin{matrix}
\boldsymbol{r}_{w,1} \\
\vdots \\
\boldsymbol{r}_{w,N_s} \\
\end{matrix}
\right] \in \mathbb{R}^{N_s\cdot N_y}. \label{yfr-def}\tag{4}\]

<p>Using these definitions, we can now write the residual vector as</p>

\[\boldsymbol{r}_w (\boldsymbol{p}) = \widetilde{\boldsymbol{W}} (\boldsymbol{y}-\boldsymbol{f}(\boldsymbol{p})) \label{rw-calc}\tag{5},\]

<p>where \(\widetilde{\boldsymbol{W}}\) has block-diagonal structure with the matrix
\(\boldsymbol{W}\) on the diagonal, like so:</p>

\[\widetilde{\boldsymbol{W}} :=\left[
\begin{matrix}
\boldsymbol{W} &amp; &amp; \\
 &amp; \ddots \\
&amp; &amp; \boldsymbol{W} \\
\end{matrix}
\right] \in \mathbb{R}^{N_s\cdot N_y \times N_s \cdot N_y}. \label{wtilde-def}\tag{6}\]

<p>For our least squares minimization, we want to minimize the \(\ell_2\) norm of 
\(\boldsymbol{r}_w(\boldsymbol{p})\):</p>

\[\boldsymbol{p}^\dagger = \arg \min_{\boldsymbol{p}} \Vert\boldsymbol{r}_w (\boldsymbol{p}) \Vert^2 \label{p-dagger-def}\tag{7}.\]

<p>The goal of this article is to give an expression for the covariance matrix of the best fit parameters
\(\boldsymbol{p}^\dagger\).</p>

<p>Before we jump into those
calculations, note that I mentioned in the previous article that this vector-oriented
approach is less benefitial <em>when implementing</em> VarPro than a more matrix oriented approach.
This is true, but mathematically both approaches are equivalent. Thus, the 
following calculations will be true regardless how the residuals are actually calculated.</p>

<h1 id="jacobian-of-the-residuals">Jacobian of the Residuals</h1>

<p>The first step when calculating the covariance matrix is to calculate the Jacobian
matrix \(\boldsymbol{J}_{r_w}\) of the weighted residuals. Let’s express it
in terms of the Jacobian matrix \(\boldsymbol{J}_{f}\) of \(\boldsymbol{f}\):</p>

\[\boldsymbol{J}_{r_w}(\boldsymbol{p}) := \frac{\partial \boldsymbol{r}_w}{\partial \boldsymbol{p}} (\boldsymbol{p})= -\widetilde{\boldsymbol{W}}\boldsymbol{J}_{f}(\boldsymbol{p})\in \mathbb{R}^{(N_s\cdot N_y) \times N_p}, \label{j-rw-def}\tag{8}\]

<p>where</p>

\[N_p:=(N_s\cdot N_c+N_\alpha) \label{Np-def}\tag{9}\]

<p>is the total number of parameters. We can write \(\boldsymbol{J}_f\) as</p>

\[\boldsymbol{J}_f(\boldsymbol{p}) := 
\frac{\partial \boldsymbol{f}}{\partial \boldsymbol{p}}(\boldsymbol{p}) =
\left[
\begin{matrix}
\boldsymbol{J}_{f_1} (\boldsymbol{p})\\
\vdots \\
\boldsymbol{J}_{f_{N_s}} (\boldsymbol{p})\\
\end{matrix}
\right], \label{jf-def}\tag{10}\]

<p>where \(\boldsymbol{J}_{f_k}= \frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{p}}\)
is the Jacobian of \(\boldsymbol{f}_k\). Since the parameter vector \(\boldsymbol{p}\) is
written as in \(\eqref{p-def}\), we can write the Jacobian of \(\boldsymbol{f}_k\)
as</p>

\[\boldsymbol{J}_{f_k} (\boldsymbol{p})= \frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{p}}(\boldsymbol{p}) =
\left[\begin{matrix}
\frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{c_1}} (\boldsymbol{p})&amp; \dots &amp; \frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{c_k}} (\boldsymbol{p})&amp; \dots &amp; \frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{c_{N_s}}} (\boldsymbol{p})&amp; \frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{\alpha}}(\boldsymbol{p})\\
\end{matrix}\right]. \label{jfk-mat}\tag{11}\]

<p>There are two pieces to this expression that we need to look at separately. 
Firstly, there is \(\frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{c_j}}\),
which simplifies to</p>

\[\frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{c_j}}(\boldsymbol{p}) =
\left\{
\begin{matrix}
\boldsymbol{\Phi}(\boldsymbol{\alpha}) &amp; ,\; j = k \\
\boldsymbol{0}_{N_y \times N_c} &amp; ,\; j \ne k .\\
\end{matrix}  \label{dfk-dcj}\tag{12}
\right.\]

<p>Next, there is the expression \(\frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{\alpha}}\).
There is a way to write this expression in tensor form, but keeping it in matrix
notation comes easier to me. So let’s do that and call it \(\boldsymbol{B}_k\).</p>

\[\boldsymbol{B}_k (\boldsymbol{\alpha})
:= \frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{\alpha}} (\boldsymbol{\alpha})
= \left[
\begin{matrix}
\frac{\partial \boldsymbol{\Phi}(\boldsymbol{\alpha})}{\partial \alpha_1} \boldsymbol{c}_k &amp; \dots &amp;
\frac{\partial \boldsymbol{\Phi}(\boldsymbol{\alpha})}{\partial \alpha_{N_\alpha}} \boldsymbol{c}_k
\end{matrix}
\right]
\in \mathbb{R}^{N_y \times N_\alpha}  \label{bk-def}\tag{13}\]

<p>Plugging eqns. \(\eqref{dfk-dcj}\) and \(\eqref{bk-def}\) into \(\eqref{jfk-mat}\)
gives us a block diagnal matrix with \(N_s+1\) blocks. Its last block is the matrix
\(\boldsymbol{B}_k\), while all the other blocks are of size \(N_y \times N_c\).
The block at index \(k\) is \(\boldsymbol{\Phi}(\boldsymbol{\alpha})\) and all the
other blocks are zero-matrices of the same size. In matrix notation this
becomes:</p>

\[\frac{\partial \boldsymbol{f}_k}{\partial \boldsymbol{p}}(\boldsymbol{p}) =
\left[\begin{matrix}
\underbrace{\boldsymbol{0}_{N_y \times N_c}}_{\text{block } 1} &amp; \dots &amp; \boldsymbol{0}_{N_y \times N_c} 
&amp; \underbrace{\boldsymbol{\Phi}(\boldsymbol{\alpha})}_{\text{block } k} &amp;
\boldsymbol{0}_{N_y \times N_c} &amp; \dots &amp; \underbrace{\boldsymbol{0}_{N_y \times N_c}}_{\text{block } N_s}&amp; 
\underbrace{\boldsymbol{B}_k(\boldsymbol{\alpha})}_{\text{block } N_s+1} 
\end{matrix}\right].  \label{jfk-block}\tag{14}\]

<p>Now, to obtain the Jacobian \(\boldsymbol{J}_f\) of \(\boldsymbol{f}(\boldsymbol{p})\), we
need to stack the individual Jacobians on top of each other as seen in eq. \(\eqref{jf-def}\).
This will create a matrix like this:</p>

\[\boldsymbol{J}_f(\boldsymbol{p}) = \frac{\partial \boldsymbol{f}}{\partial \boldsymbol{p}}(\boldsymbol{p}) =
\left[\begin{matrix}
\underbrace{
\begin{matrix}
\boldsymbol{\Phi}(\boldsymbol{\alpha}) &amp; &amp;  \\
 &amp; \ddots &amp; &amp; \\
 &amp; &amp; \boldsymbol{\Phi}(\boldsymbol{\alpha}) \\
\end{matrix}
}_{N_s \times N_s \text{ blocks}}
&amp;
\begin{matrix}
\boldsymbol{B}_1(\boldsymbol{\alpha}) \\
\vdots \\
\boldsymbol{B}_{N_s}(\boldsymbol{\alpha}) \\
\end{matrix}
\end{matrix}\right], \tag{15}\label{jac-f}\]

<p>where the blocks that are left blank are zeros of appropriate size. Finally,
this gives us the Jacobian of \(\boldsymbol{r}_w\) using eq. \(\eqref{j-rw-def}\):</p>

\[\boldsymbol{J}_{r_w}(\boldsymbol{p}) = - \widetilde{\boldsymbol{W}} \boldsymbol{J}_{f}(\boldsymbol{p}) =
-\left[\begin{matrix}
\boldsymbol{W}\boldsymbol{\Phi}(\boldsymbol{\alpha}) &amp; &amp; &amp;\boldsymbol{W} \boldsymbol{B}_1(\boldsymbol{\alpha})  \\
 &amp; \ddots &amp; &amp;  \vdots \\
 &amp; &amp;\boldsymbol{W} \boldsymbol{\Phi}(\boldsymbol{\alpha}) &amp;\boldsymbol{W} \boldsymbol{B}_{N_s}(\boldsymbol{\alpha}) 
\end{matrix}\right] \tag{16}\label{jac-rw}\]

<h1 id="the-covariance-matrix-of-the-best-fit-parameters">The Covariance Matrix of the Best Fit Parameters</h1>

<blockquote>
  <p><span style="font-variant:small-caps;">Remark on Notation</span></p>

  <p>From now on, we’ll see a couple of long-ish expressions involving parametrized
matrices. I’ll omit the parameters in the matrices inside the expression
and just append them once at the end. So instead of \(\boldsymbol{X}(\boldsymbol{p})\boldsymbol{Y}(\boldsymbol{p})\),
I’ll just write \(\boldsymbol{X}\boldsymbol{Y}(\boldsymbol{p})\). I’ll do this
for all expressions involving matrix products, even including transposes
and inverses.</p>
</blockquote>

<p>For the following calculations, it’s helpful to rewrite eq. \(\eqref{jac-rw}\) by introducing the
block diagonal matrix \(\boldsymbol{A}(\boldsymbol{\alpha})\) and the block
matrix \(\boldsymbol{B}(\boldsymbol{\alpha})\) such that</p>

\[\begin{eqnarray}
\boldsymbol{J}_{r_w}(\boldsymbol{p}) &amp;=&amp; -\left[ 
\begin{matrix}
\boldsymbol{A}(\boldsymbol{\alpha}) &amp; \boldsymbol{B}(\boldsymbol{\alpha})
\end{matrix}
 \right],  \tag{17} \label{jac-rw-ab} \\
\\
\boldsymbol{A}(\boldsymbol{\alpha}) &amp;:=&amp;
\left[\begin{matrix}
\boldsymbol{W}\boldsymbol{\Phi}(\boldsymbol{\alpha}) &amp; &amp;  \\
 &amp; \ddots &amp; \\
 &amp; &amp;\boldsymbol{W} \boldsymbol{\Phi}(\boldsymbol{\alpha}) 
\end{matrix}\right],\;
\boldsymbol{B}(\boldsymbol{\alpha}) :=
\left[\begin{matrix}
\boldsymbol{W} \boldsymbol{B}_1(\boldsymbol{\alpha})  \\
\vdots  \\
\boldsymbol{W} \boldsymbol{B}_{N_s}(\boldsymbol{\alpha}) 
\end{matrix}\right]. \tag{18}\label{def-ab}
\end{eqnarray}\]

<p>From my article on <a href="/blog/2024/bayesian-nonlinear-least-squares/">Bayesian Nonlinear Least Squares</a>,
we know that we can write the covariance matrix \(\boldsymbol{C}_{p^\dagger}\) of
the best fit parameters \(\boldsymbol{p}\) as</p>

\[\boldsymbol{C}_{p^\dagger} = \hat{\sigma} \left(\boldsymbol{J}^T_{r_w} \boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger) \right)^{-1} \in \mathbb{R}^{N_p \times N_p} \label{cov-jrw}\tag{19},\]

<p>where \(\hat{\sigma}\) is a scalar that depends on our prior assumptions. Let’s
find an expression for this step by step, starting with the matrix product
\(\boldsymbol{J}^T_{r_w} \boldsymbol{J}_{r_w}\).</p>

\[\begin{eqnarray}
&amp;\boldsymbol{J}^T_{r_w}&amp; \boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger) =
\left[
\begin{matrix}
\boldsymbol{A}^T \boldsymbol{A}(\boldsymbol{\alpha}^\dagger) &amp; \boldsymbol{A}^T \boldsymbol{B}(\boldsymbol{\alpha}^\dagger) \\
\boldsymbol{B}^T \boldsymbol{A}(\boldsymbol{\alpha}^\dagger) &amp; \boldsymbol{B}^T \boldsymbol{B}(\boldsymbol{\alpha}^\dagger) \\
\end{matrix}
\right] \label{jtj-ab} \tag{20} \\
\\
&amp;=&amp; \left[
\begin{matrix}
(\boldsymbol{W}\boldsymbol{\Phi})^T \boldsymbol{W}\boldsymbol{\Phi}(\boldsymbol{\alpha}^\dagger)  &amp; &amp; &amp; (\boldsymbol{W}\boldsymbol{\Phi})^T W\boldsymbol{B}_1(\boldsymbol{\alpha}^\dagger) \\
&amp;\ddots  &amp; &amp; \vdots \\
&amp; &amp; (\boldsymbol{W}\boldsymbol{\Phi})^T \boldsymbol{W}\boldsymbol{\Phi}(\boldsymbol{\alpha}^\dagger)   &amp; (\boldsymbol{W}\boldsymbol{\Phi})^T W\boldsymbol{B}_{N_s}(\boldsymbol{\alpha}^\dagger) \\
(W\boldsymbol{B}_1)^T \boldsymbol{W}\boldsymbol{\Phi}(\boldsymbol{\alpha}^\dagger)  &amp; \dots &amp; (W\boldsymbol{B}_{N_s})^T \boldsymbol{W}\boldsymbol{\Phi}(\boldsymbol{\alpha}^\dagger) &amp; \sum_{k=1}^{N_s}(W\boldsymbol{B}_{k})^T W\boldsymbol{B}_{k}(\boldsymbol{\alpha}^\dagger) \\
\end{matrix} \label{jtj-full} \tag{21}
\right], 
\end{eqnarray}\]

<p>where \(\boldsymbol{\alpha}^\dagger\) are the best-fit nonlinear parameters.
We could be tempted to invert eq. \(\eqref{jtj-full}\) directly to get to the covariance matrix
in eq. \(\eqref{cov-jrw}\). But keep in mind that it’s an \(N_p \times N_p\) matrix,
where \(N_p = N_s \cdot N_c + N_\alpha\).
That’s not a big deal if we only have a tiny number of right hand sides and 
linear coefficients. If, however, the number of linear coefficients \(N_c\)
becomes large, say \(\mathcal{O}(10)\), and / or the
number of right hand sides \(N_s\) becomes huge, 
say \(\mathcal{O}(10^5)\), then this matrix quickly reaches billions or even
trillions of elements. That’s why it makes sense to exploit the special structure
of the matrix a bit more to help calculate its inverse.</p>

<p>Since \(\boldsymbol{J}^T_{r_w} \boldsymbol{J}_{r_w}\) is a \(2 \times 2\) block
diagonal matrix, we can use it’s <a href="https://en.wikipedia.org/wiki/Schur_complement#Properties">Schur complement</a>
to help us express the inverse. It’s Schur complement with respect to it’s upper
left block is defined as:</p>

\[\boldsymbol{S}(\boldsymbol{\alpha}) := \boldsymbol{B}^T \boldsymbol{B}(\boldsymbol{\alpha}^\dagger) -\boldsymbol{B}^T \boldsymbol{A}  (\boldsymbol{A}^T \boldsymbol{A})^{-1}\boldsymbol{A}^T \boldsymbol{B}(\boldsymbol{\alpha}^\dagger) \in \mathbb{R}^{N_\alpha \times N_\alpha}. \label{schur}\tag{22}\]

<p>where the inverse to \((\boldsymbol{A}^T \boldsymbol{A})\) must exist<sup id="fnref:inverse" role="doc-noteref"><a href="#fn:inverse" class="footnote" rel="footnote">1</a></sup><sup>,</sup><sup id="fnref:existence" role="doc-noteref"><a href="#fn:existence" class="footnote" rel="footnote">2</a></sup>. Since
\((\boldsymbol{A}^T \boldsymbol{A})\) is a block diagonal matrix containing
\((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi} (\boldsymbol{\alpha}^\dagger)\) 
on the diagonal, it’s actually very simple to invert:</p>

\[(\boldsymbol{A}^T \boldsymbol{A})^{-1}(\boldsymbol{\alpha}) =
\left[\begin{matrix}
((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi})^{-1} (\boldsymbol{\alpha}^\dagger)&amp; &amp;  \\
 &amp; \ddots &amp; \\
 &amp; &amp;((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi})^{-1} (\boldsymbol{\alpha}^\dagger)
\end{matrix}\right]. \label{ata-inv} \tag{23}\]

<p>For this expression, we only need to calculate the inverse of the relatively small matrix
\((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi} \in \mathbb{R}^{N_c \times N_c}\),
which for a typical number of basefunctions only has tens or hundreds of elements,
rather than billions. This allows us to simplify the expression for the Schur
complement considerably. After a bit of calculating, we come up with:</p>

\[\begin{eqnarray}
\boldsymbol{S}(\boldsymbol{\alpha}^\dagger) := &amp;\sum_{k=1}^{N_s}&amp;\left[(W\boldsymbol{B}_{k})^T W\boldsymbol{B}_{k}(\boldsymbol{\alpha}^\dagger)\right. \\
 &amp;+&amp;\left.((\boldsymbol{W \Phi})^T \boldsymbol{W B}_k)^T ((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi})^{-1} (\boldsymbol{W \Phi})^T \boldsymbol{W B}_k)(\boldsymbol{\alpha}^\dagger)\right]. \label{schur-sum}\tag{24} 
\end{eqnarray}\]

<p>That thing might not look like a simplification at first, but remember it’s essentially a 
sum of pretty small \(N_\alpha \times N_\alpha\) matrices. Also the inverse
of \(((\boldsymbol{W \Phi})^{-1}\boldsymbol{W \Phi})\) does not change with \(k\)
and can be pre-calculated. Further, note that the matrix product on the right
hand side has the structure \(\boldsymbol{X}^T \boldsymbol{YX}\). Note also that
for our case \(\boldsymbol{S}(\boldsymbol{\alpha}^\dagger)  = \boldsymbol{S}^T(\boldsymbol{\alpha}^\dagger)\),
i.e. the Schur complement is symmetric.</p>

<p>Using its Schur complement, we can give an 
<a href="https://en.wikipedia.org/wiki/Schur_complement#Properties">expression for the inverse</a>
of \(\boldsymbol{J}_{r_w}^T\boldsymbol{J}_{r_w}\) in a block diagonal form:</p>

\[(\boldsymbol{J}_{r_w}^T\boldsymbol{J}_{r_w})^{-1}(\boldsymbol{p}^\dagger) =
\left[
\begin{matrix}
(\boldsymbol{A}^T \boldsymbol{A})^{-1}(\boldsymbol{\alpha}^\dagger) + \boldsymbol{G}\boldsymbol{S}^{-1}\boldsymbol{G^T}(\boldsymbol{\alpha}^\dagger) &amp; -\boldsymbol{G S}^{-1}(\boldsymbol{\alpha}^\dagger) \\
- (\boldsymbol{G S}^{-1}(\boldsymbol{\alpha}^\dagger))^T &amp; \boldsymbol{S}^{-1}(\boldsymbol{\alpha}^\dagger) \label{jtj-inv-schur} \tag{25}\\
\end{matrix}
\right],\]

<p>where we have introduced the matrix \(\boldsymbol{G}\) as</p>

\[\boldsymbol{G}(\boldsymbol{\alpha}^\dagger) 
=(\boldsymbol{A}^T \boldsymbol{A})^{-1} \boldsymbol{A}^T \boldsymbol{B} (\boldsymbol{\alpha}^\dagger)
=\left[
\begin{matrix}
((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi})^{-1}(\boldsymbol{W \Phi})^T \boldsymbol{WB}_1(\boldsymbol{\alpha}^\dagger) \\
\vdots \\
((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi})^{-1}(\boldsymbol{W \Phi})^T \boldsymbol{WB}_{N_s}(\boldsymbol{\alpha}^\dagger) \label{g-def}\tag{26} \\
\end{matrix}
\right]\]

<p>The neat thing about calculating the inverse of \(\boldsymbol{J}_{r_w}^T\boldsymbol{J}_{r_w}\)
via eq. \(\eqref{jtj-inv-schur}\) is, that we only have to calculate the inverse
of relatively small matrices: \(\boldsymbol{S}^{-1}\) is pretty small and so is
\(((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi})^{-1}\), which we can use to build
the inverse of \(\boldsymbol{A}^T\boldsymbol{A}\). This, in turn, gives us a
numerically efficient (and probably more stable) way of calculating the
covariance matrix using eq. \(\eqref{cov-jrw}\). There might be even further
simplifications that we can exploit, but I’ll leave it at that for now<sup id="fnref:pseudoinverse" role="doc-noteref"><a href="#fn:pseudoinverse" class="footnote" rel="footnote">3</a></sup>.</p>

<h1 id="endnotes">Endnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:inverse" role="doc-endnote">
      <p>One can see in the expressions below, that this inverse exists if \(\boldsymbol{W \Phi}(\boldsymbol{\alpha}^\dagger)\) has linear independent columns. That means the base functions (at the best fit parameters) must be linearly independent, which should be the case for a correcly chosen set of basefunctions for VarPro. <a href="#fnref:inverse" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:existence" role="doc-endnote">
      <p>Note that \((\boldsymbol{A}^T\boldsymbol{A})^{-1}\) existing is necessary but not sufficient for the existence of \((\boldsymbol{J}_{r_w}^T\boldsymbol{J}_{r_w})^{-1}\). We assume the latter also exists, because we need it to calculate the covariance matrix. <a href="#fnref:existence" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:pseudoinverse" role="doc-endnote">
      <p>Note, for example that some expressions involve \(((\boldsymbol{W \Phi})^T \boldsymbol{W \Phi})^{-1}(\boldsymbol{W \Phi})^T\), which is the pseudoinverse of \(\boldsymbol{W \Phi}\). We can use SVD or QR decompositions to obtain the solutions rather than calculating the peudoinverse itself. <a href="#fnref:pseudoinverse" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="least-squares" /><category term="algorithm" /><category term="varpro" /><summary type="html"><![CDATA[In this article, I explore how to efficiently calculate the covariance matrix of the best fit parameters for global fitting problems that use the variable projection (VarPro) algorithm. It’s a very niche topic, but I do need it for my open source library so I might as well write it down. It might be helpful for people looking to gain a deeper understanding of the math behind VarPro.]]></summary></entry><entry><title type="html">Nonlinear Least Squares Fitting - A Bayesian Tutorial</title><link href="https://geo-ant.github.io/blog/2024/bayesian-nonlinear-least-squares/" rel="alternate" type="text/html" title="Nonlinear Least Squares Fitting - A Bayesian Tutorial" /><published>2024-07-18T00:00:00+00:00</published><updated>2024-07-18T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2024/bayesian-nonlinear-least-squares</id><content type="html" xml:base="https://geo-ant.github.io/blog/2024/bayesian-nonlinear-least-squares/"><![CDATA[<p>In this article, we’ll derive from scratch the well known formulas –and some not so well known
ones– for nonlinear least squares fitting from a Bayesian perspective.
We’ll be using only elementary linear algebra and elementary calculus. It turns
out, that this is a valuable exercise, because it allows us to clearly state our
assumptions about the problem and assign unambigous meaning to all components of
the least squares fitting process.</p>

<p>I went into this rabbit hole when trying to understand why two well-respected
optimization libraries, namely <a href="https://www.gnu.org/software/gsl/doc/html/nls.html#covariance-matrix-of-best-fit-parameters">the GSL</a>
and Google’s <a href="http://ceres-solver.org/nnls_covariance.html">Ceres Solver</a>
give two slightly different results for the covariance matrices of the best
fit parameters. As it turns out, both are correct, but they imply slightly 
different states of prior knowledge about the problem.</p>

<h1 id="1-nonlinear-least-squares-fitting">1. Nonlinear Least Squares Fitting</h1>

<p>Assume we have a vector of \(N_y\) observations \(\boldsymbol{y} \in \mathbb{R}^{N_y}\)
that we want to “fit” with a <em>model function</em>
\(\boldsymbol{f}(\boldsymbol{p}) \in \mathbb{R}^{N_y}\) that depends on \(N_p\)
parameters \(\boldsymbol{p} \in \mathbb{R}^{N_p}\). Then, the process of least
squares <em>fitting</em> a function is to find the parameters that minimize the weighted
squared difference of the function and the observations. We call those parameters
\(\boldsymbol{p}^\dagger\), formally:</p>

\[\boldsymbol{p}^\dagger = \arg\min_{\boldsymbol{p}} \frac{1}{2} \lVert W \left(\boldsymbol{y} - \boldsymbol{f}(\boldsymbol{p})\right) \rVert^2 \label{lsqr-fitting}\tag{1.1},\]

<p>where \(\lVert\cdot\rVert\) is the \(\ell_2\) norm of a vector and \(\boldsymbol{W} \in \mathbb{R}^{N_y \times N_y}\) is a matrix
of weights. We’ll spend much more time on what those weights mean in
the following sections. For now, let’s introduce some helpful abbreviations
and rewrite the equation above:</p>

\[\boldsymbol{p}^\dagger = \arg\min_{\boldsymbol{p}} g(\boldsymbol{p}) \label{lsqr-fitting-g}\tag{1.2},\]

<p>where \(g\) is called the <em>objective function</em> of the minimization, which
we can write in terms of the (weighted) residuals:</p>

\[\begin{eqnarray} 
g(\boldsymbol{p}) &amp;:=&amp; \frac{1}{2} \boldsymbol{r}_w^T(\boldsymbol{p}) \boldsymbol{r_w}(\boldsymbol{p}), \label{objective-function} \tag{1.3}\\
  &amp;=&amp; \frac{1}{2} \boldsymbol{r}^T(\boldsymbol{p}) (\boldsymbol{W}^T \boldsymbol{W}) \boldsymbol{r}(\boldsymbol{p}) \\
\boldsymbol{r}(\boldsymbol{p}) &amp;:=&amp; \boldsymbol{y}-\boldsymbol{f}(\boldsymbol{p}) \label{residuals}\tag{1.4} \\
\boldsymbol{r}_w(\boldsymbol{p}) &amp;:=&amp; \boldsymbol{W} \boldsymbol{r}(\boldsymbol{p}) \label{weighted-residuals}\tag{1.5}
\end{eqnarray}\]

<p>Note, that the objective function is a quadratic form with the matrix
\(\boldsymbol{W}^T\boldsymbol{W}\). Sometimes, the objective function
is written as \(\boldsymbol{r}^T \boldsymbol{W'} \boldsymbol{r}\) or
\(\boldsymbol{r}^T \boldsymbol{S^{-1}} \boldsymbol{r}\). All forms are equivalent,
but it does influence how exactly the elements of the weighting matrix appear
in the later equations. This determines their precise meaning. One can always transform
one representation into another, but the important thing is to pick one and 
apply it consistently. For the purposes of this article, let’s stick with the
objective function as stated above.</p>

<p>Our ultimate goal here is to answer the following questions: why do we minimize
the sum of squares, as opposed to e.g. the sum of absolutes or 4th powers?
What exactly do the weights represent? What are the statistical
properties of the best fit parameters? What is the
confidence band (or <em>credible interval</em> to be more precise) around the best model
after the fit? We’ll try and answer this by building nonlinear least squares from the ground up.</p>

<h2 id="11-helpful-identities">1.1 Helpful Identities</h2>

<p>Before we dive in to the Bayesian perspective, let’s state some helpful equations
that will come in handy later. These formulas are found e.g. in the classic (Noc06)
or the very nice thesis (Kal22). The gradient of the objective function is</p>

\[\nabla g(\boldsymbol{p}) = \boldsymbol{J}^T_{r_w}(\boldsymbol{p})\; \boldsymbol{r}_w(\boldsymbol{p}), \label{gradient-objective}\tag{1.6}\]

<p>where \(\boldsymbol{J}_{r_w}(\boldsymbol{p})\) is the Jacobian Matrix of 
the weighted residuals \(\boldsymbol{r}_w(\boldsymbol{p})\).
Often, the Jacobian is only denoted by a simple \(\boldsymbol{J}\), but for this post
it pays to be precise, because there is also the Jacobian \(\boldsymbol{J}_f\) of 
the model function \(\boldsymbol{f}(\boldsymbol{p})\), which is related to \(\boldsymbol{J}_{r_w}\)
by</p>

\[\boldsymbol{J}_{r_w}(\boldsymbol{p}) = -\boldsymbol{W} \boldsymbol{J}_f(\boldsymbol{p}).\label{jac-rw-f}\tag{1.7}\]

<p>The Hessian matrix of \(g(\boldsymbol{p})\) is calculated as follows, using
the notation \(r_{w,j}\) for the element at index \(j\) of \(\boldsymbol{r}_w\):</p>

\[\begin{eqnarray}
\boldsymbol{H}_{g}(\boldsymbol{p}) = \nabla^2 g(\boldsymbol{p}) &amp;=&amp; \boldsymbol{J}_{r_w}^T(\boldsymbol{p}) \boldsymbol{J}_{r_w}(\boldsymbol{p})+\sum_{j} r_{w,j}(\boldsymbol{p}) \nabla^2 r_{w,j}(\boldsymbol{p}) \label{hessian-g-exact} \tag{1.8} \\
 &amp;\approx&amp; \boldsymbol{J}_{r_w}^T(\boldsymbol{p}) \boldsymbol{J}_{r_w}(\boldsymbol{p}) \label{hessian-g-approx} \tag{1.9},
\end{eqnarray}\]

<p>The approximation is very commonly used in least squares fitting and we will
also employ it in this article. With those bits of housekeeping out of the way,
let’s now dive into the Bayesian perspective.</p>

<h1 id="2-bayesian-perspective-on-least-squares">2. Bayesian Perspective on Least Squares</h1>

<p>The fundamental assumption that will bring us to nonlinear least squares is
that the data can be modeled by a normal distribution. Why is that? One way
to think of it, is that each observation is the sum of a <em>true</em> underlying value
with additive zero mean Gaussian noise.</p>

\[Y_j = Y_{j,\text{true}} + N_j,\]

<p>where the noise \(N \sim \mathcal{N}(0,\sigma_j)\) is normally distributed with
zero mean. If we assume the true value is one exact value, we can model its distribution as a delta
peak. Thus, the sum of the true value and the noise will follow a normal
distribution centered around the true value. In our case, we don’t know the
true value, but we assume it can be modeled by \(\boldsymbol{f}(\boldsymbol{p})\),
for an unknown set of parameters \(\boldsymbol{p}\). 
Let me reiterate what that means: we assume that there is a value of \(\boldsymbol{p}\) such that for this value,
the model \(\boldsymbol{f}(\boldsymbol{p})\) actually produces the true underlying values.</p>

<p>Under these assumptions, if the parameters \(\boldsymbol{p}\) are given, the likelihood of observing data \(\boldsymbol{y}\) is
given as a <a href="https://en.wikipedia.org/wiki/Multivariate_normal_distribution">multivariate normal distribution</a>
with mean \(\boldsymbol{f}(\boldsymbol{p})\) and covariance matrix \(\Sigma \in \mathbb{R}^{N_y\times N_y}\). 
The covariance matrix informs us about the observed variance (and covariance) <em>in the observed data</em>. For
example, on the \(j\)-th position on the diagonal it has the <em>variance</em> \(\sigma_j^2\)
of the data element at \(j\).</p>

<p>For this article, let’s make the assumption that the elements
\(y_j\) are <a href="https://en.wikipedia.org/wiki/Independence_(probability_theory)">statistically independent</a>
of each other. Some of the calculations in this article will be true even if they
aren’t, but finding  out which ones is left as an <em>exercise to the reader</em><sup id="fnref:exercise-reader" role="doc-noteref"><a href="#fn:exercise-reader" class="footnote" rel="footnote">1</a></sup>.
The statistical independence implies that the covariance matrix is nonzero
only on the diagonal:</p>

\[\Sigma = \text{diag}(\sigma^2_1,\dots,\sigma^2_{N_y}) \label{covariance-diag}\tag{2.1}\]

<p>We can now write the likelihood of observing data \(y_j\) at index \(j\), given
the parameters \(\boldsymbol{p}\) and the standard deviation \(\sigma_j\) at \(j\) as:</p>

\[P(y_j | \boldsymbol{p}, \sigma_j) = \frac{1}{\sqrt{2\pi}\sigma_j}\exp\left( -\frac{1}{2}\frac{(y_j-f_j(\boldsymbol{p}))^2}{\sigma_j^2} \right).\label{likelihood-yi}\tag{2.2}\]

<p>This allows us to write the likelihood of observing the data vector \(\boldsymbol{y}\)
given the parameters \(\boldsymbol{p}\) and the set of standard deviations \(\{\sigma_j\}\) 
as the product:</p>

\[P(\boldsymbol{y}|\boldsymbol{p},\{\sigma_j\}) = \prod_j P(y_j|\boldsymbol{p},\{\sigma_j\}) \label{likelihood} \tag{2.3}\]

<p>From a Bayesian point of view, we are interested
in the posterior distribution that describes the probability density of \(\boldsymbol{p}\)
given our observations \(\boldsymbol{y}\). Bayes Theorem brings us a step closer towards
this:</p>

\[P(\boldsymbol{p},\{\sigma_j\}|\boldsymbol{y}) = \frac{P(\boldsymbol{y}|\boldsymbol{p},\{\sigma_j\})\cdot P(\boldsymbol{p},\{\sigma_j\})}{P(\boldsymbol{y})}.\label{joint-posterior}\tag{2.4}\]

<p>This is actually the joint posterior probability for the parameters \(\boldsymbol{p}\) <em>and</em>
the standard deviations \(\{\sigma_j\}\). 
Annoyingly, this posterior still contains the standard deviations. What’s nice is, that
we can <a href="https://en.wikipedia.org/wiki/Marginal_distribution">marginalize out</a>
the dependency on the standard deviations in the posterior by integration:</p>

\[P(\boldsymbol{p}|\boldsymbol{y}) \propto \int  \left(\prod_j P(y_j|\boldsymbol{p},\{\sigma_j\})\right)\cdot P(\boldsymbol{p},\{\sigma_j\}) \;\text{d}\{\sigma_j\}\label{posterior}\tag{2.5}, \\\]

<p>where the integral is multidimensinal over all \(\sigma_j\). This is finally 
the posterior distribution for the parameters that we are looking for.
It allows us, for example, to find the maximum <em>a posteriori</em> estimate for the parameters.
There is just one problem: to actually find an expression for that, we need to
solve the integral. And to do that we have to make some assumptions about
the <em>prior</em> probability distribution \(P(\boldsymbol{p},\{\sigma_j\})\).</p>

<p>In the next sections,
we’ll work through the implications of different priors
and we’ll relate them to the least squares problem eq. \(\eqref{lsqr-fitting}\).
We’ll see, how different assumptions end up giving us the same estimate for the best
(i.e. the most probable) parameters, but that there are differences in the associated uncertainties.
Let’s start with the simplest case first.</p>

<h1 id="3-nonlinear-least-squares-with-known-standard-deviations">3. Nonlinear Least Squares with Known Standard Deviations</h1>

<p>If, for some reason, we <em>know</em> the standard deviations \(\{\sigma_j\}\), then
things become decently simple. First, let’s rewrite the joint prior distribution 
as</p>

\[P(\boldsymbol{p},\{\sigma_j\}) = P(\boldsymbol{p}|\sigma_j)\,P(\{\sigma_j\}) = P(\boldsymbol{p})\,\prod_j P(\sigma_j).\]

<p>Here, we have used that \(\boldsymbol{p}\) and \(\sigma_j\) are statistically
independent in our case. Since we know all the standard deviations, the
probability densities \(P(\sigma_j)\) are delta-functions centered around
the known values, such that the posterior eq. \(\eqref{posterior}\) becomes:</p>

\[P(\boldsymbol{p}|\boldsymbol{y}) \propto \prod_j P(y_j|\boldsymbol{p},\sigma_j)\cdot P(\boldsymbol{p}), \tag{3.1}\]

<p>where all the \(\sigma_j\) are known constants. Now, we further assume 
a uniform (or <em>flat</em>) prior for the parameters:</p>

\[P(\boldsymbol{p}) = \text{const.} \tag{3.2}\]

<p>From a Bayesian perspective, this is a naive way of conveying ignorance about
the values of the parameters <sup id="fnref:uniform-prior" role="doc-noteref"><a href="#fn:uniform-prior" class="footnote" rel="footnote">2</a></sup><sup>,</sup><sup id="fnref:ignorance" role="doc-noteref"><a href="#fn:ignorance" class="footnote" rel="footnote">3</a></sup>.
This lets us write the joint posterior probability eq. \(\eqref{joint-posterior}\)
as:</p>

\[\begin{eqnarray}
P(\boldsymbol{p}&amp;|&amp;\boldsymbol{y}) \propto \exp\left(- g(\boldsymbol{p})\right) \label{posterior-known-sigma}\tag{3.4} \\
\text{with } &amp;\boldsymbol{W}&amp; = \text{diag}(1/\sigma_1,\dots,1/\sigma_{N_y}) \label{weights-known-sigma}\tag{3.5}
\end{eqnarray}\]

<p>and with \(g(\boldsymbol{p})\) as in eq. \(\eqref{objective-function}\).
It is now trivial to show, that maximizing the posterior distribution is equivalent
to the least squares problem \(\eqref{lsqr-fitting}\). It’s important to note that
the weights must be a diagonal matrix as in eq. \(\eqref{weights-known-sigma}\).</p>

<h2 id="31-the-covariance-matrix-for-the-best-fit-parameters">3.1 The Covariance Matrix for the Best Fit Parameters</h2>

<p>We will now derive an approximation for the covariance matrix \(\boldsymbol{C}_{p^\dagger}\)
of the best fit parameters. This matrix is of interest e.g. because on the diagonal 
it contains the variances of the elements of the parameter vector. If we know
those, we can give estimates of credible intervals for the parameters. We can
also use the covariance matrix to calculate <a href="https://en.wikipedia.org/wiki/Correlation#Correlation_matrices">correlations</a>
between the parameters. Let’s start by examining the posterior probability
for the parameters.</p>

<p>In general, we won’t be able to say much about the functional form of the
posterior probability \(\eqref{posterior-known-sigma}\),
because it depends on \(\boldsymbol{f}(\boldsymbol{p})\). However, in the vicinity
of the best fit parameters \(\boldsymbol{p}^\dagger\), we can make some 
useful approximations.</p>

<p>Using a Taylor expansion we can write \(g\) around the best fit parameter \(\boldsymbol{p}^\dagger\)
as</p>

\[g(\boldsymbol{p}) = g(\boldsymbol{p}^\dagger) + \nabla g(\boldsymbol{p}^\dagger) \cdot (\boldsymbol{p}-\boldsymbol{p}^\dagger) + \frac{1}{2} (\boldsymbol{p}-\boldsymbol{p}^\dagger)^T\, \boldsymbol{H}_g(\boldsymbol{p}^\dagger) (\boldsymbol{p}-\boldsymbol{p}^\dagger) + \mathcal{O}(\lVert \boldsymbol{p}-\boldsymbol{p}^\dagger\rVert^3)\]

<p>where \(\nabla g(\boldsymbol{p}^\dagger)\) is the gradient of \(g\) and \(\boldsymbol{H}_g(\boldsymbol{p}^\dagger)\)
is the Hessian of \(g\), both evaluated at \(\boldsymbol{p}^\dagger\). Since
\(\boldsymbol{p}^\dagger\) minimizes \(g\), we know that the gradient must vanish, 
such that we can approximate \(g\) up to second order as</p>

\[g(\boldsymbol{p}) \approx g(\boldsymbol{p}^\dagger) + \frac{1}{2} (\boldsymbol{p}-\boldsymbol{p}^\dagger)^T\, \boldsymbol{H}_g(\boldsymbol{p}^\dagger) (\boldsymbol{p}-\boldsymbol{p}^\dagger). \label{taylor-approx-g}\tag{3.6}\]

<p>We can use these results to approximate the posterior probability distribution
around the best fit parameters:</p>

\[P(\boldsymbol{p}|\boldsymbol{y}) \approx K \cdot \exp\left(-\frac{1}{2} (\boldsymbol{p}-\boldsymbol{p}^\dagger)^T\, \boldsymbol{H}_g(\boldsymbol{p}^\dagger) (\boldsymbol{p}-\boldsymbol{p}^\dagger)\right), \label{posterior-p-approximation}\tag{3.7}\]

<p>where \(K\in\mathbb{R}\) is a constant of integration that also absorbs the constant
first term in \(\eqref{taylor-approx-g}\). This turns out to be the a 
<a href="https://en.wikipedia.org/wiki/Multivariate_normal_distribution">multivariate Gaussian distribution</a>
with expected value \(\boldsymbol{p}^\dagger\) and covariance matrix 
\(\boldsymbol{C}_{p^\dagger}=\boldsymbol{H}_g^{-1}(\boldsymbol{p}^\dagger)\).</p>

\[\boldsymbol{C}_{p^\dagger} = \boldsymbol{H}_g^{-1}(\boldsymbol{p}^\dagger) \approx \left( \boldsymbol{J}_{r_w}^T (\boldsymbol{p}^\dagger) \boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger)\right)^{-1} = (\boldsymbol{J}_f^T(\boldsymbol{p}^\dagger) \; \boldsymbol{W}^T\boldsymbol{W} \;\boldsymbol{J}_f(\boldsymbol{p}^\dagger))^{-1}, \label{covariance-matrix-known-weights} \tag{3.9}\]

<p>where \(\boldsymbol{J}_f\) is the Jacobian of \(\boldsymbol{f}(\boldsymbol{p})\)
and thus \(\boldsymbol{J}_{r_w} = -\boldsymbol{W}\boldsymbol{J}_f\). We
have used the common approximation for the Hessian of \(g\) as given in eq. \(\eqref{hessian-g-approx}\).
Note, that eq. \(\eqref{covariance-matrix-known-weights}\) is the covariance for <em>known standard deviations</em>, where the weights
must be chosen as specified in \(\eqref{weights-known-sigma}\).
This concludes our analysis for the case of known standard deviations for now. Let’s 
turn to cases where we don’t know the standard deviations.</p>

<h1 id="4-nonlinear-least-squares-with-unknown-standard-deviations-with-a-known-relative-scaling">4. Nonlinear Least Squares with Unknown Standard Deviations with a Known Relative Scaling</h1>

<p>Okay, the section heading is a mouthful, but it’s important to be precise. We’ll examine
a special case of unknown standard deviations, which is as follows: Assume that
the exact standard deviations are unknown, but that we (again, <em>for some reason</em>) 
know a scaling between them, formally:</p>

\[\sigma_j = w_j \, \sigma, \label{relative-scaling} \tag{4.1}\]

<p>where \(\sigma\in\mathbb{R}\) is unknown, but the relative scaling \(w_j\) <em>is</em> known. We’ll
see, that the naming of \(w_j\) is not an accident, because if we set</p>

\[\boldsymbol{W} = \text{diag}(1/w_1,\dots,1/w_{N_y}) \label{relative-weights-matrix}\tag{4.2},\]

<p>then we can rewrite the posterior as:</p>

\[\begin{eqnarray}
P(\boldsymbol{p} | \boldsymbol{y}) &amp;\propto&amp; \int_0^\infty \frac{1}{\sigma^{N_y}}\exp\left(-\frac{1}{2\sigma^2} \sum_{j=1}^{N_y} \frac{(y_j - f_j(\boldsymbol{p}))^2}{w_j^2} \right) P(\boldsymbol{p},\sigma)\; \text{d}\sigma\label{posterior-rel-sigma}\tag{4.3} \\
 &amp;=&amp; \int_0^\infty \frac{1}{\sigma^{N_y}}\exp\left(-\frac{1}{2\sigma^2} \lVert \boldsymbol{r}_w(\boldsymbol{p})\rVert^2\right) P(\boldsymbol{p},\sigma)\; \text{d}\sigma, \label{posterior-rel-sigma-r2} \tag{4.4}
\end{eqnarray}\]

<p>where \(\boldsymbol{W}\) must be defined as in eq. \(\eqref{relative-weights-matrix}\) and
\(\boldsymbol{r}_w\) is defined as in eq. \(\eqref{weighted-residuals}\). This is
already a much simpler expression than the more general posterior in eq. \(\eqref{posterior}\),
since the integral now is only along the one scalar \(\sigma\). However, we still need
a joint prior distribution for \(P(\boldsymbol{p},\sigma)\).</p>

<h2 id="41-uniform-prior">4.1 Uniform Prior</h2>

<p>Since the uniform prior for \(P(\boldsymbol{p})\) did pretty well for us in the
case of known standard deviations, let’s see what we can achieve when we assume
a uniform prior for the joint distribution like so:</p>

\[P(\boldsymbol{p},\sigma) = \text{const.}\]

<p>This leads us (using a <a href="https://www.wolframalpha.com/input?i=int%281%2Fsigma%5EN*exp%28-1%2F%282*sigma%5E2%29*+r%5E2%29%29">little help from Wolfram Alpha</a>)
to the following expression for the posterior distribution, where we have omitted all constants:</p>

\[P(\boldsymbol{p}| \boldsymbol{y}) \propto (\lVert \boldsymbol{r}_w(\boldsymbol{p})\rVert^2)^{-\frac{N_y-1}{2}}. \label{posterior-uniform-prior}\tag{4.5}\]

<p>Glossing over the fact that this is possibly an <a href="https://onlinelibrary.wiley.com/doi/full/10.1111/sjos.12550">improper posterior</a>,
we can already see that the posterior probability is maximal exactly at the least squares
estimate \(\boldsymbol{p}^\dagger\) as given in the introduction in eq. \(\eqref{lsqr-fitting}\).</p>

<h3 id="412-laplaces-approximation">4.1.2 Laplace’s Approximation</h3>

<p>Now, we’ll employ a common step in Bayesian analysis to make the posterior
probability more tractable: we’ll approximate it as a Gaussian. This approximation can,
in principle, be applied to all posterior probabilities with varying degrees of
accuracy. It’s called <a href="https://en.wikipedia.org/wiki/Laplace%27s_approximation">Laplace’s Approximation</a>
and we will quickly go through it step by step, independent of our specific
posterior. We’ll get back to eq. \(\eqref{posterior-uniform-prior}\) later.
To start, we define the negative log-posterior as</p>

\[L(\boldsymbol{p}) = -\log P(\boldsymbol{p}|\boldsymbol{y}). \label{log-posterior}\tag{4.6}\]

<p>Maximizing the posterior probability with respect to the parameters is obviously
the same as minimizing the negative log-posterior. Let’s denote the parameter that
minimizes \(L\) with \(\boldsymbol{p}^\star\). Note that in our particular case 
\(\boldsymbol{p}^\star=\boldsymbol{p}^\dagger\) is just the least squares estimate
as defined in eq. \(\eqref{lsqr-fitting}\), but in general</p>

\[\boldsymbol{p}^\star = \arg \min_{\boldsymbol{p}} L(\boldsymbol{p}).\]

<p>Now, just like we did <a href="/blog/2024/bayesian-nonlinear-least-squares/#31-the-covariance-matrix-for-the-best-fit-parameters">in this section</a>,
we can approximate \(L(\boldsymbol{p})\) by a second order Taylor expansion around
it’s minimum</p>

\[L(\boldsymbol{p}) \approx L(\boldsymbol{p}^\star) + \frac{1}{2} (\boldsymbol{p}-\boldsymbol{p}^\star)^T\, \boldsymbol{H}_L(\boldsymbol{p}^\star) (\boldsymbol{p}-\boldsymbol{p}^\star), \label{taylor-approx-L}\tag{4.7}\]

<p>where \(\boldsymbol{H}_L(\boldsymbol{p}^\star)\) is the Hessian of \(L\) evaluated
at the minimum \(\boldsymbol{p}^\star\). By inverting eq. \(\eqref{log-posterior}\)
we can approximate the posterior as</p>

\[P(\boldsymbol{p}|\boldsymbol{y}) \approx K^\star \cdot \exp\left(-\frac{1}{2} (\boldsymbol{p}-\boldsymbol{p}^\star)^T\, \boldsymbol{H}_L(\boldsymbol{p}^\star) (\boldsymbol{p}-\boldsymbol{p}^\star) \right),\label{posterior-p-approximation-L}\tag{4.8}\]

<p>which is a multivariate normal distribution with covariance matrix</p>

\[\boldsymbol{C}_{p^\star} = \boldsymbol{H}_L^{-1}(\boldsymbol{p}^\star). \label{covariance-HL}\tag{4.9}\]

<p>That means we can estimate the covariance of the best fit parameters under this
approximation as the inverse of the Hessian of \(L\) at the best fit parameters.</p>

<h3 id="413-covariance-of-the-best-fit-parameters-for-a-uniform-prior">4.1.3 Covariance of the Best Fit Parameters for a Uniform Prior</h3>

<p>To find the covariance matrix for the best fit parameters given our prior, we
have to calculate \(L(\boldsymbol{p})\) and its Hessian at the best fit parameters.</p>

\[L(\boldsymbol{p}) = -\log P(\boldsymbol{p}|\boldsymbol{y}) = \frac{N_y-1}{2} \log \lVert \boldsymbol{r}(\boldsymbol{p})\rVert^2. \label{L-uniform}\tag{4.10}\]

<p>After some calculations (which I have relegated to Appendix A), we see that
we can approximate the Hessian as</p>

\[\boldsymbol{H}_L(\boldsymbol{p}^\dagger)\approx \frac{N_y-1}{\lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2}\boldsymbol{J}^T_{r_w}(\boldsymbol{p}^\dagger) \boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger) \label{hessian-uniform}\tag{4.11},\]

<p>which finally leads us to an approximation of the covariance matrix of the best fit
parameters as</p>

\[\boldsymbol{C}_{p^\dagger} \approx \frac{\lVert \boldsymbol{r}(\boldsymbol{p^\dagger})\rVert^2}{N_y-1} (\boldsymbol{J}^T_{r_w}(\boldsymbol{p}^\dagger) \boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger))^{-1}. \label{cov-uniform}\tag{4.12}\]

<p>Note, that this particular expression for the covariance matrix holds only for the assumptions
made in section 4.1 up to here. I’ll have more to say about the exact form of the
covariance matrix, but I’ll say it in a dedicated section further below. Next,
let’s see how the choice of prior influences our estimation of the covariance
matrix of the best fit parameters.</p>

<h2 id="42-jeffreys-prior">4.2 Jeffreys’ Prior</h2>

<p><a href="https://en.wikipedia.org/wiki/Jeffreys_prior">Jeffreys’ prior</a> is another commonly
used prior distribution that conveys gross ignorance about the scale of unknown
parameters (Gel13, sections 2.8, 3.2). After performing the necessary calculations,
which are tedious and therefore relegated to Appendix B, we obtain this prior
as:</p>

\[P(\boldsymbol{p},\sigma) \propto \frac{1}{\sigma^{N_p+1}} \sqrt{\text{det}\boldsymbol{J}_f^T(\boldsymbol{p}) \boldsymbol{W}^T \boldsymbol{W} \boldsymbol{J}_f(\boldsymbol{p})} \label{jeffreys-prior}\tag{4.13},\]

<p>where \(\boldsymbol{J}_f\) is the Jacobian of \(f\) and \(\boldsymbol{W}\) is the
weight matrix as defined in eq. \(\eqref{relative-weights-matrix}\). To obtain the
posterior, we can again <a href="https://www.wolframalpha.com/input?i=int%281%2Fsigma%5E%28N%2BM%2B1%29*exp%28-1%2F%282*sigma%5E2%29*+r%5E2%29%29">use Wolfram Alpha</a>:</p>

\[P(\boldsymbol{p}|\boldsymbol{y}) \propto  \sqrt{\text{det}\boldsymbol{J}_f^T(\boldsymbol{p}) \boldsymbol{W}^T \boldsymbol{W} \boldsymbol{J}_f(\boldsymbol{p})} \cdot (\lVert\boldsymbol{r}_w(\boldsymbol{p})\rVert^2)^{-\frac{N_y+N_p}{2}}. \label{posterior-jeffreys}\tag{4.14}\]

<p>Strictly speaking that is all we can say about the posterior, since the first
factor also depends on \(\boldsymbol{p}\). However, there are a few assumptions we
can make to approximate the posterior.</p>

<p>The second factor \((\lVert\boldsymbol{r}_w(\boldsymbol{p})\rVert^2)^{-\frac{N_y+N_p}{2}}\) in eq. \(\eqref{posterior-jeffreys}\) will typically be a very sharp
peak around the least squares estimate \(\boldsymbol{p}^\dagger\).
At least for a sufficienly large number of data points. Notice, that 
\(\boldsymbol{J}_f^T (\boldsymbol{W}^T \boldsymbol{W})^{-1} \boldsymbol{J}_f\)
is approximately \(\boldsymbol{W}^2 \boldsymbol{H}_f\), due to the diagonal structure
of \(\boldsymbol{W}\), where \(\boldsymbol{H}_f\) is the Hessian of \(f\). It is 
reasonable to assume that for most well-behaved functions, the determinant of the
Hessian won’t exhibit a behavior that substantially alters the shape of the posterior.
This is consistent with the fact that Jeffreys’s prior is a <em>noninformative</em> prior,
which means that it should not greatly alter the value of the <em>maximum a posteriori</em>
estimate, compared to the value of the maximum likelihood estimate (Gel13, sections 2.8, 3.2).
Since the second factor is likely a narrow peak around \(\boldsymbol{p}^\dagger\),
we can approximate the value \(\boldsymbol{J}_f^T (\boldsymbol{W}^T \boldsymbol{W})^{-1} \boldsymbol{J}_f\)
by its value at \(\boldsymbol{p}^\dagger\), which is a constant. This finally
leads us to an approximation of the posterior around \(\boldsymbol{p}^\dagger\)
as:</p>

\[P(\boldsymbol{p}|\boldsymbol{y}) \approx K^\dagger \cdot (\lVert\boldsymbol{r}_w(\boldsymbol{p})\rVert^2)^{-\frac{N_y+N_p}{2}}, \label{posterior-jeffreys-approx}\tag{4.15}\]

<p>where we’ve absorbed all constants of proportionality into \(K^\dagger\). Now,
using Laplace’s Approximation again, we can approximate the posterior as a normal
distribution with the following covariance matrix:</p>

\[\boldsymbol{C}_{p^\dagger} \approx \frac{\lVert \boldsymbol{r}(\boldsymbol{p^\dagger})\rVert^2}{N_y+N_p} (\boldsymbol{J}^T_{r_w}(\boldsymbol{p}^\dagger) \boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger))^{-1}. \label{cov-jeffreys}\tag{4.15}\]

<p>Calculating the Hessian works analogous to the calculations in Appendix A. We’ll
summarize and discuss the results so far in the next section.</p>

<h1 id="5-summary-posteriors-best-parameters-and-covariances">5. Summary: Posteriors, Best Parameters, and Covariances</h1>

<p>Okay, now let’s take a step back and see how our results can be summarized and
examine how our results are linked to the least squares fitting approach from
our introductory section. We have approximated all our posteriors in the form 
of normal distributions</p>

\[P(\boldsymbol{p}|\boldsymbol{y}) \approx K' \cdot \exp\left(-\frac{1}{2} (\boldsymbol{p}-\boldsymbol{p}^\dagger)^T\, \boldsymbol{C}_{p^\dagger}^{-1} (\boldsymbol{p}-\boldsymbol{p}^\dagger) \right),\label{posterior-approximation-generalized}\tag{5.1}\]

<p>with appropriate constants \(K'\). The <em>maximum a posteriori</em> estimate for all
cases was given as the minimizer of the sum of squared residuals eq. \(\eqref{lsqr-fitting}\):</p>

\[\boldsymbol{p}^\dagger = \arg\min_{\boldsymbol{p}} \frac{1}{2} \lVert W \left(\boldsymbol{y} - \boldsymbol{f}(\boldsymbol{p})\right) \rVert^2 \tag{5.2},\]

<p>where the choice of the weight matrix \(\boldsymbol{W} \in \mathbb{R}^{N_y \times N_y}\)
depends on our choice of prior. We have also calculated the covariance matrices
for the best fit parameters \(\boldsymbol{p}^\dagger\), which also depend on our
choice of prior. However, all of them can be generalized as</p>

\[\boldsymbol{C}_{p^\dagger} \approx \hat{\sigma}^2 \, (\boldsymbol{J}^T_{r_w}(\boldsymbol{p}^\dagger) \boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger))^{-1} = \hat{\sigma}^2 \, (\boldsymbol{J}_f^T(\boldsymbol{p}^\dagger) \; \boldsymbol{W}^T\boldsymbol{W} \;\boldsymbol{J}_f(\boldsymbol{p}^\dagger))^{-1} , \label{cov-sigmat-hat}\tag{5.3}\]

<p>with a scalar \(\hat{\sigma}^2 \in \mathbb{R}\) depending on our choice of prior. As before,
\(\boldsymbol{J}_{r_w}\) is the Jacobian of \(\boldsymbol{r}_w\) and
\(\boldsymbol{J}_{f}\) is the Jacobian of the model function
\(\boldsymbol{f}(\boldsymbol{p})\). The following table shows how the weight
matrix and the factor \(\hat{\sigma}^2\) are related to priors:</p>

<table>
    <style>
        table {
            width: 100%;
            border-collapse: collapse;
            caption-side: bottom; /* Position the caption at the bottom */
        }
        caption {
            text-align: left;
        }
        th, td {
            border: 1px solid black;
            padding: 8px;
            text-align: left;
        }
    </style>
	<tbody>
		<tr>
			<th><b>Prior Knowledge of Std Devs.</b></th>
			<th>$$\hat{\sigma}^2$$</th>
			<th>$$\boldsymbol{W}$$</th>
			<th><b>Remarks</b></th>
		</tr>
		<tr>
			<td>Known <br /> (w/ Uniform prior for \(\boldsymbol{p}\))</td>
			<td>\(1\)</td>
			<td>\(\text{diag}(1/\sigma_1,\dots,1/\sigma_{N_y})\)</td>
			<td>\(\sigma_j\): known standard deviation at index \(j\)</td>
		</tr>
		<tr>
			<td>Known Relative Scaling <br /> (Uniform Prior for \(\boldsymbol{p},\sigma\))</td>
			<td>\(\frac{\lVert \boldsymbol{r}(\boldsymbol{p^\dagger})\rVert^2}{N_y-1}\)</td>
			<td rowspan="2">\(\text{diag}(1/w_1,\dots,1/w_{N_y})\)</td>
			<td rowspan="2">Absolute magnitude of standard deviations is unknown, but we know \(\sigma_j = w_j \sigma\), with \(w_j\) known and \(\sigma\) unknown.</td>
		</tr>
		<tr>
			<td>Known Relative Scaling <br /> (Jeffreys' Prior for \(\boldsymbol{p},\sigma\))</td>
			<td>\(\frac{\lVert \boldsymbol{r}(\boldsymbol{p^\dagger})\rVert^2}{N_y+N_p}\)</td>
		</tr>
	</tbody>
  <caption>Table 1: Influence of the choice of prior on the weights and the covariance matrix.</caption>
</table>

<p>Since all of our posterior distributions are given as normal distributions (at
least approximately), the covariance matrices allow us to give credible intervals
for our best fit parameters<sup id="fnref:covpar" role="doc-noteref"><a href="#fn:covpar" class="footnote" rel="footnote">4</a></sup>. While our choice of prior does not influence the best
fit parameters themselves, we can see that it does influence the credible intervals
around them.</p>

<h2 id="52-discussing-the-covariance-and-comparison-to-prior-art">5.2 Discussing the Covariance and Comparison to Prior Art</h2>

<p>Although this is already a long article, I want to briefly discuss the values
for \(\hat{\sigma}^2\) for unknown standard deviations. For uniform priors,
the value of \(\hat{\sigma}^2\) is the well-known
<a href="https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation">estimator of sample variance</a>
around the best fit parameters. The result for the Jeffreys’s prior is interesting
because it looks very similar, but the denominator is \(N_y+N_p\) instead of
\(N_y-1\). This is noteworthy, but for typical least squares problems where \(N_p \ll N_y\), this won’t
make much of a difference. However, it’s important
to restate, that all our derivations assumed that the model is indeed the
correct one. Thus, this result doesn’t imply that using
models with more parameters to fit unknown data is better than using models with
fewer parameters.</p>

<p>If you’ve used least squares fitting libraries, you might have come across
a strikingly similar formula for the covariance matrix, with the difference that 
\(\hat{\sigma}^2=\lVert \boldsymbol{r}(\boldsymbol{p^\dagger})\rVert^2/(N_y-N_p)\),
where \(N_y - N_p\) is typically called the <em>degrees of freedom</em> of the fit. This
formula has always seemed reasonable to me, probably because I’ve gotten used to it,
but I have never seen it derived. I assume it’s a Frequentist result, but
I honestly don’t know. It might just as well be a Bayesian
result, obtained with different priors or different approximations.
Again, for \(N_p \ll N_y\) the differences are probably neglegible.
However, I feel it’s valuable to understand, which assumptions those
formulas imply: for example, <a href="http://ceres-solver.org/nnls_covariance.html">Ceres Solver</a> 
gives the same formulas that we derived for known standard derivations, while the
<a href="https://www.gnu.org/software/gsl/doc/html/nls.html#covariance-matrix-of-best-fit-parameters">GNU scientific library</a> (GSL)
gives the (suspected) frequentist \(\hat{\sigma}^2\).</p>

<p>Initially, I had assumed that either Jeffreys’ prior or the uniform prior will
give me the \(\hat{\sigma}^2\) that GSL uses and I was surprised neither of them did.
I’m pretty confident we can construct a prior that gives us that expression,
maybe using a conjugate prior (which would be an Inverse-Gamma) for \(\sigma\), with
carefully chosen parameters and a uniform prior for \(\boldsymbol{p}\). However, I don’t know how I can
argue that this is a good prior, other than that it produces the results I expected.
That might or might not be a good argument, because I don’t know why I expected
those results in the first place.</p>

<h1 id="6-credible-intervals-for-the-best-fit-model">6. Credible Intervals for the Best Fit Model</h1>

<p>Before we embark on this final step, let’s look at what we have done above,
when we gave expressions for the covariance matrix: we have related
how the uncertainties in our observations \(\boldsymbol{y}\)
propagate to the uncertainties of our best fit parameters 
\(\boldsymbol{p}^\dagger\). The covariance matrix of the best fit parameters 
allows us to give confidence intervals of the parameters via their standard deviations,
since the posterior distributions for the parameters were approximately normal.</p>

<p>Now, we will do the same thing again, in a way. We’ll find a way to approximate the
posterior for our best fit solution \(\boldsymbol{f}^\dagger = \boldsymbol{f}(\boldsymbol{p^\dagger})\)
as a normal distribution. Then, we will calculate the covariance matrix,
which gives us the standard deviations, for the elements of \(\boldsymbol{f}^\dagger\).
If we know those, we can give credible intervals around the entries of our best
fit solution, which together make up the credible band.</p>

<p>We treat \(\boldsymbol{z}=\boldsymbol{f}(\boldsymbol{p})\) as a new variable
and we are interested in the probability distribution of \(\boldsymbol{z}\).
We can obtain it from the posterior probability distribution of \(\boldsymbol{p}\)
by performing a <a href="https://en.wikipedia.org/wiki/Probability_density_function#Function_of_random_variables_and_change_of_variables_in_the_probability_density_function">change of variables</a>,
where I am following the notation of Sivia and Skilling (Siv06):</p>

\[P(\boldsymbol{z}|\boldsymbol{y}) = P(\boldsymbol{p}=\boldsymbol{f}^{-1}(\boldsymbol{z})|\boldsymbol{y})\cdot \text{det} \frac{d\boldsymbol p}{d \boldsymbol{z}} \label{change-of-var}\tag{6.1},\]

<p>where the second factor on the right hand side is the determinant of the Jacobian of \(\boldsymbol{p}\)
with respect to \(\boldsymbol{z}\). To get a grip on this expression, we approximate \(\boldsymbol{y}\)
around the best fit parameters by a first order Taylor series:</p>

\[\boldsymbol{z} = \boldsymbol{f}(\boldsymbol{p}) \approx \boldsymbol{f}(\boldsymbol{p}^\dagger) + \boldsymbol{J}_f(\boldsymbol{p}^\dagger)\,(\boldsymbol{p}-\boldsymbol{p}^\dagger), \tag{6.2}\]

<p>where \(\boldsymbol{J}_f(\boldsymbol{p}^\dagger)\) is the Jacobian of \(\boldsymbol{f}(\boldsymbol{p})\)
evaluated at \(\boldsymbol{p}^\dagger\). Note, that there is nothing about \(\boldsymbol{p}^\dagger\)
that would make this approximation particularly well-suited and we
use it because it is analytically convenient<sup id="fnref:approx-f" role="doc-noteref"><a href="#fn:approx-f" class="footnote" rel="footnote">5</a></sup>. This allows us to write</p>

\[\boldsymbol{p} = \boldsymbol{f}^{-1}(\boldsymbol{z}) \approx \boldsymbol{J}_f^{-1}(\boldsymbol{p}^\dagger) (\boldsymbol{z}-\boldsymbol{f}(\boldsymbol{p}^\dagger))+\boldsymbol{p}^\dagger \label{p-of-z} \tag{6.3}\]

<p>Now we use eq. \(\eqref{p-of-z}\) and our approximation for the posterior eq. 
\(\eqref{posterior-approximation-generalized}\) and plug it into eq. \(\eqref{change-of-var}\).
Note, that have to choose the covariance matrix \(\boldsymbol{C}_{p^\dagger}\) that corresponds
to our prior assumptions, but the general functional form of the posterior is
always the same. This leads us to:</p>

\[\begin{eqnarray}
P(\boldsymbol{z}|\boldsymbol{y}) &amp;\approx&amp; K''\cdot \exp\left(-\frac{1}{2} (\boldsymbol{J_f}^{-1}(\boldsymbol{p}^\dagger)(\boldsymbol{z}-\boldsymbol{f}(\boldsymbol{p}^\dagger)))^T\, \boldsymbol{C}_{p^\dagger}^{-1} (\boldsymbol{J_f}^{-1}(\boldsymbol{p}^\dagger)(\boldsymbol{z}-\boldsymbol{f}(\boldsymbol{p}^\dagger))) \right) \\
  &amp;=&amp;  K''\cdot \exp\left(-\frac{1}{2} (\boldsymbol{z}-\boldsymbol{f}(\boldsymbol{p}^\dagger))^T\, (\boldsymbol{J_f}^T)^{-1}(\boldsymbol{p}^\dagger) \boldsymbol{C}_{p^\dagger}^{-1} \boldsymbol{J_f}^{-1}(\boldsymbol{p}^\dagger)(\boldsymbol{z}-\boldsymbol{f}(\boldsymbol{p}^\dagger)) \right) \\
  &amp;=&amp;  K''\cdot \exp\left(-\frac{1}{2} (\boldsymbol{z}-\boldsymbol{f}(\boldsymbol{p}^\dagger))^T\, (\boldsymbol{J_f}(\boldsymbol{p}^\dagger) \boldsymbol{C}_{p^\dagger} \boldsymbol{J_f}^{T}(\boldsymbol{p}^\dagger))^{-1} (\boldsymbol{z}-\boldsymbol{f}(\boldsymbol{p}^\dagger)) \right) \\
\end{eqnarray}\]

<p>where we have absorbed all constant factors into \(K''\). Yet again, this is
a <a href="https://en.wikipedia.org/wiki/Multivariate_normal_distribution">multivariate normal</a>,
with a covariance matrix \(\boldsymbol{C}_{f^\dagger}\) given by</p>

\[\boldsymbol{C}_{f^\dagger} = \boldsymbol{J_f}(\boldsymbol{p}^\dagger) \boldsymbol{C}_{p^\dagger} \boldsymbol{J_f}^{T}(\boldsymbol{p}^\dagger) \label{cov-f}\tag{6.4}.\]

<p>In essence, we have derived the law of <a href="https://en.wikipedia.org/wiki/Propagation_of_uncertainty">Propagation of Uncertainty</a>
for our special case<sup id="fnref:prop-uncertainty" role="doc-noteref"><a href="#fn:prop-uncertainty" class="footnote" rel="footnote">6</a></sup>. Eq. \(\eqref{cov-f}\) is the desired covariance
of our best fit model function values.</p>

<h2 id="61-from-covariance-matrix-to-credible-intervals">6.1 From Covariance Matrix to Credible Intervals</h2>

<p>So now that we have the covariance matrix, how do we use it to construct
<a href="https://en.wikipedia.org/wiki/Credible_interval#Contrasts_with_confidence_interval">credible intervals</a>
around the best fit function? Luckily we already know everything we need.</p>

<p>We have approximated our best fit solution \(\boldsymbol{f}^\dagger= \boldsymbol{f}(\boldsymbol{p^\dagger})\) 
as normally distributed with a covariance matrix of \(\boldsymbol{C}_{f^\dagger}\). That means
that the variance for each entry \(f_j^\dagger\) of \(\boldsymbol{f}^\dagger\) is on index \(j\)
of the diagonal of \(\boldsymbol{C}_{f^\dagger}\). Let’s express this in vector notation:</p>

\[(\sigma_{f_1^\dagger}^2,\dots,\sigma^2_{f_{N_y}^\dagger})^T = \text{diag} (\boldsymbol{C}_{f^\dagger}). \label{variance-vector}\tag{6.5}\]

<p>It would be numerically wasteful to calculate the whole matrix \(\boldsymbol{C}_{f^\dagger}\)
just to then immediately discard everything except the diagonal elements. There
is a better way. To see this, we write the Jacobian as a collection using row-vectors:</p>

\[\boldsymbol{J}_f(\boldsymbol{p}^\dagger) = \begin{bmatrix}
- \boldsymbol{j}_1^T -\\
- \boldsymbol{j}_2^T -\\
\vdots \\
- \boldsymbol{j}_n^T -
\end{bmatrix},\]

<p>where \(\boldsymbol{j}_i^T\) is the \(i\)-th <em>row</em> of the Jacobian of \(\boldsymbol{f}\) at the
best fit parameters. Now we can write the variance for each element of \(\boldsymbol{f}\)
as</p>

\[\sigma_{f_i^\dagger}^2 = \boldsymbol{j}_i^T \boldsymbol{C}_{p^\dagger} \boldsymbol{j}_i, \label{efficient-sigma-f}\tag{6.6}\]

<p>where, again \(\boldsymbol{j}_i^T\) is a <em>row</em> vector representing a <em>row</em> of the Jacobian.
Wolberg arrives at the same formula using a slightly different approach (Wol06, section 2.5).
Using this way of calculating the variances saves a significant amount of computations
and should be preferred to calculating the complete matrix product. The credible
intervals for a given probability can now be obtained using the quantile function
of the normal distribution. So for each element \(f_i^\dagger\) of the best fit,
we know that the value is in the following range with a probability of \(\rho \in (0,1)\):</p>

\[f_i^\dagger \pm \Phi^{-1}\left(\frac{\rho+1}{2}\right) \cdot \sigma_{f_i^\dagger}  \tag{6.7}\]

<p>where \(\Phi^-1\) is the <a href="https://en.wikipedia.org/wiki/Normal_distribution#Quantile_function">quantile function of the normal distribution</a>.
Using this, we can calculate this interval for each of the elements of the best fit, which
will give us a credible band around the best fit model.</p>

<p>As a final note to this already very long article, let’s note that Wolberg gives
an almost identical expression for the confidence bands around the best fit
parameters (Wol06, section 2.6). The only difference is that he uses the 
quantile function of Student’s t-distribution instead of the Gaussian quantile
function, but unfortunately no rationale is provided for that. I can only speculate
that this is due to his expressions being <em>confidence bands</em>. Those are a Frequentist concept
and they <a href="https://en.wikipedia.org/wiki/Credible_interval#Contrasts_with_confidence_interval">don’t typically align</a>
with Bayesian credible intervals, although they serve a similar conceptual purpose.</p>

<h1 id="conclusion">Conclusion</h1>

<p>I know this article was a <em>tour de force</em> through the method of nonlinear least squares
fitting from the ground up. I hope that seeing it like this helps others (as it helped
me) to demystify and understand the method and especially the statistical analysis
of the parameters and the best fit model. The latter part is often overlooked. 
If your usecase is not covered by this article, I hope that you’ll find the tools
here to modify the calculations according to your needs. And finally, if you find
errors please reach out via mail or by commenting below.</p>

<h1 id="references">References</h1>
<p>(Noc06) J Nocedal &amp; SJ Wright: “Numerical Optimization”, Springer, 2nd ed, 2006</p>

<p>(Siv06) D Sivia &amp; J Skilling: “Data Analysis - A Bayesian Tutorial”, Oxford University Press, 2<sup>nd</sup> ed, 2006</p>

<p>(Wol06) J Wolberg: “Data Analysis Using the Method of Least Squares”, Springer, 2006</p>

<p>(Gel13) A Gelman <em>et al</em>: “Bayesian Data Analysis”, CRC Press, 3<sup>rd</sup> ed, 2013 (<a href="http://www.stat.columbia.edu/~gelman/book/">freely available from the author</a>)</p>

<p>(Kal22) M Kaltenbach: “The Levenberg-Marquardt Method and its Implementation in Python”, Diploma Thesis, Uni Konstanz, 2022, (<a href="http://nbn-resolving.de/urn:nbn:de:bsz:352-2-1ofyba49ud2jr5">link</a>)</p>

<h1 id="appendix">Appendix</h1>

<h2 id="a-calculating-the-hessian-of-log-lvert-boldsymbolrboldsymbolp-rvert2">A. Calculating the Hessian of \(\log \lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\)</h2>

<p>To derive eq. \(\eqref{hessian-uniform}\) we need to calculate the Hessian of 
\(\log \lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\). Let’s do the calculation
element-wise by repeatedly applying the chain rule.</p>

\[\begin{eqnarray}
\boldsymbol{H}\{\log \lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\}_{kl} &amp;=&amp; \frac{\partial^2}{\partial p_k \partial p_l}\log\lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2 \\
 &amp;=&amp; \frac{\partial}{\partial p_k} \left(\frac{1}{\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2} \cdot \frac{\partial}{\partial p_l}\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2\right) \\
 &amp;=&amp; \frac{\partial}{\partial p_k} \frac{1}{\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^{2}}\cdot \frac{\partial}{\partial p_l}\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2+\frac{\partial^2}{\partial p_k\partial p_l}\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2 \\ 
 &amp;=&amp; \frac{\partial}{\partial p_k} \frac{1}{\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^{2}}\cdot \frac{\partial}{\partial p_l}\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2+ \frac{1}{\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2}\boldsymbol{H}\{\lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\}_{kl}\\ 
\end{eqnarray}\]

<p>We are interested in evaluating the Hessian at an extremum of \(\log \lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\),
which implies that \(\nabla \log\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2=\boldsymbol{0}\),
which implies \(\nabla \lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2=\boldsymbol{0}\),
which implies \(\frac{\partial}{\partial p_l} \lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2=0\)
for all indices \(l\). That makes the first term in the equation vanish, so that
we can write the Hessian of the log transformed sum of squares as:</p>

\[\boldsymbol{H}\{\log \lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\}(\boldsymbol{p}^\dagger) = \frac{1}{\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2}\boldsymbol{H}\{\lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\}(\boldsymbol{p}^\dagger)\]

<p>We know an approximation for the Hessian of \(\frac{1}{2}\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2\)
from eq. \(\eqref{hessian-g-approx}\), so that we can write:</p>

\[\boldsymbol{H}\{\log \lVert \boldsymbol{r}(\boldsymbol{p}) \rVert^2\}(\boldsymbol{p}^\dagger) \approx \frac{2}{\lVert\boldsymbol{r}(\boldsymbol{p}) \rVert^2} \boldsymbol{J}_{r_w}^T(\boldsymbol{p}^\dagger)\boldsymbol{J}_{r_w}(\boldsymbol{p}^\dagger)\]

<p>Now  \(\eqref{hessian-uniform}\) follows trivially.</p>

<h2 id="b-calculating-jeffreys-prior">B. Calculating Jeffreys’ Prior</h2>

<p>To calculate <a href="https://en.wikipedia.org/wiki/Jeffreys_prior">Jeffreys’ prior</a> for
the likelihood in \(\eqref{posterior-rel-sigma-r2}\), we
have to calculate the Fisher information matrix. For a given likelihood
\(P(\boldsymbol{y}|\boldsymbol{\theta})\), where \(\boldsymbol{\theta}\)
are the parameters, the elements of the <a href="https://en.wikipedia.org/wiki/Fisher_information#Matrix_form">Fisher information matrix</a>
\(\boldsymbol{I}(\boldsymbol{\theta}\) are given as:</p>

\[[\boldsymbol{I}(\boldsymbol{\theta})]_{kl} = E\left[ \left. \left(\frac{\partial}{\partial \theta_k}\log P(\boldsymbol{y}|\boldsymbol{\theta}) \right)\left(\frac{\partial}{\partial \theta_l}\log P(\boldsymbol{y}|\boldsymbol{\theta}) \right) \right| \boldsymbol{\theta}\right],\]

<p>where \(E[ \boldsymbol{\phi}(\boldsymbol{y})\vert\boldsymbol{\theta}]=\int \boldsymbol{\phi}(\boldsymbol{y}) P(\boldsymbol{y}|\boldsymbol{\theta}) \text{d}\boldsymbol{y}\) denotes the expected value 
of \(\boldsymbol{\phi}(\boldsymbol{y})\). This is a volume integral over
\(\boldsymbol{y}\), but we won’t actually have to explicitly perform the integration in the
following derivations. For the following sections, we’ll abbreviate \(E[\boldsymbol{\phi}(\boldsymbol{y})\vert\boldsymbol{\theta}]\)
as \(E[\boldsymbol{\phi}(\boldsymbol{y})]\) purely for notational convenience.</p>

<p>In our case, the parameter vector \(\boldsymbol{\theta}\) consists of the elements 
of the vector \(\boldsymbol{p}\) and the single scalar \(\sigma\):</p>

\[\boldsymbol{\theta} = (\boldsymbol{p}^T,\sigma)^T.\]

<p>That means \(\boldsymbol{\theta}\in \mathbb{R}^{N_p+1}\), which means the 
derivative \(\partial/\partial \theta_k = \partial/\partial p_k\) for \(k=1,\dots,N_p\) 
and \(\partial/\partial \theta_{N_p+1} = \partial/\partial \sigma\). That means
we can write the Fisher information matrix as a block matrix like so:</p>

\[I(\boldsymbol{p},\sigma) = \left(\begin{matrix} \boldsymbol{I}_{PP}(\boldsymbol{p},\sigma) &amp; \boldsymbol{v}_{PS}(\boldsymbol{p},\sigma) \\
                           \boldsymbol{v}_{PS}^T(\boldsymbol{p},\sigma) &amp; I_{\sigma\sigma}, \\
\end{matrix}\right)\in \mathbb{R}^{N_p+1 \;\times\; N_p+1}\]

<p>with the square matrix \(\boldsymbol{I}_{PP} \in \mathbb{R}^{N_p \times N_p}\), 
the column vector \(\boldsymbol{v}_{PS} \in \mathbb{R}^{N_p}\), and the
scalar entry \(I_{\sigma\sigma} \in \mathbb{R}\). The elements of the matrices are as follows:</p>

\[\begin{eqnarray}
[\boldsymbol{I}_{PP}(\boldsymbol{p},\sigma)]_{kl} &amp;=&amp; E\left[ \frac{\partial \mathcal{L}}{\partial p_k} \cdot \frac{\partial \mathcal{L}}{\partial p_l} \right]\\
[\boldsymbol{v}_{PS}(\boldsymbol{p},\sigma)]_k &amp;=&amp; E\left[ \frac{\partial \mathcal{L}}{\partial p_k} \cdot \frac{\partial \mathcal{L}}{\partial \sigma} \right]\\
I_{\sigma\sigma}(\boldsymbol{p},\sigma) &amp;=&amp; E\left[ \frac{\partial \mathcal{L}}{\partial \sigma} \cdot \frac{\partial \mathcal{L}}{\partial \sigma} \right]\\
\end{eqnarray}\]

<p>where we defined \(\mathcal{L}\) as follows:</p>

\[\begin{eqnarray}
\mathcal{L}(\boldsymbol{p},\sigma) &amp;:=&amp; \log P(\boldsymbol{y}|\boldsymbol{p},\sigma) \\
 &amp;=&amp; -N_y\log\sigma-\frac{1}{2\sigma^2}\sum_j \frac{(y_j-f_j(\boldsymbol{p}))^2}{w_j^2}+\text{const.}, \\
\end{eqnarray}\]

<p>which means we can calculate the partial derivatives like so:</p>

\[\begin{eqnarray}                    
\frac{\partial \mathcal{L}}{\partial \sigma} (\boldsymbol{p},\sigma) &amp;=&amp; -\frac{N_y}{\sigma}+\frac{1}{\sigma^3}\sum_j \frac{(y_j-f_j(\boldsymbol{p}))^2}{w_j^2}\\
\frac{\partial \mathcal{L}}{\partial p_k} (\boldsymbol{p},\sigma) &amp;=&amp; \frac{1}{\sigma^2}\sum_j \frac{y_j-f_j(\boldsymbol{p})}{w_j^2}\cdot\frac{\partial f_j}{\partial p_k}(\boldsymbol{p}).
\end{eqnarray}\]

<p>Now let’s calculate the elements of the matrix, starting with the 
scalar \(I_{\sigma\sigma}\).</p>

<h2 id="b1-calculating-i_sigmasigma">B.1 Calculating \(I_{\sigma\sigma}\)</h2>
<p>\(\begin{eqnarray}
I_{\sigma\sigma} &amp;=&amp; E\left[ \left( \frac{-N_y\sigma^2+\sum_j \frac{(y_j-f_j(\boldsymbol{p}))^2}{w_j^2}}{\sigma^3} \right)^2 \right]\\
 &amp;=&amp; \frac{1}{\sigma^6} E \left[ \sum_{i,j} \frac{(y_i-f_i(\boldsymbol{p}))^2 (y_j-f_j(\boldsymbol{p}))^2}{w_i^2 w_j^2}  \right] - \frac{2 N_y}{\sigma^4} E\left[\sum_j \frac{(y_j-f_j(\boldsymbol{p}))^2}{w_j^2}\right]+\frac{N_y^2}{\sigma^2} E[1] .
\end{eqnarray}\)</p>

<p>The key to calculate the expectected values inside the sums are, is to
understand that the \(y_j-f_j(\boldsymbol{p})\) are independent variables, with Gaussian
distributions centered around a mean value \(\mu_j = 0\)
and with a standard deviation of \(\sigma_j = w_j \sigma\). This allows us
to write the first term of the sum:</p>

\[\begin{eqnarray}
&amp;E&amp; \left[ \sum_{i,j} \frac{(y_i-f_i(\boldsymbol{p}))^2 (y_j-f_j(\boldsymbol{p}))^2}{w_i^2 w_j^2}  \right] = \sum_{i,j} \frac{E\left[(y_i-f_i(\boldsymbol{p}))^2 (y_j-f_j(\boldsymbol{p}))^2\right]}{w_i^2 w_j^2} \\
&amp;=&amp; \sum_{i,j; i=j} \frac{E\left[(y_i-f_i(\boldsymbol{p}))^2 (y_j-f_j(\boldsymbol{p}))^2\right]}{w_i^2 w_j^2} + \sum_{i,j; i\neq j} \frac{E\left[(y_i-f_i(\boldsymbol{p}))^2 (y_j-f_j(\boldsymbol{p}))^2\right]}{w_i^2 w_j^2} \\
&amp;=&amp; \sum_{i} \frac{E\left[(y_i-f_i(\boldsymbol{p}))^4\right]}{w_i^4} + \sum_{i,j; i\neq j} \frac{E\left[(y_i-f_i(\boldsymbol{p}))^2\right] E\left[(y_j-f_j(\boldsymbol{p}))^2\right]}{w_i^2 w_j^2} \\
&amp;=&amp; \sum_{i} \frac{3\sigma_i^4}{w_i^4} + \sum_{i,j; i\neq j} \frac{\sigma_i^2 \sigma_j^2}{w_i^2 w_j^2} 
= 3\sigma^4 \,\sum_{i} 1+ \sigma^4 \sum_{i,j; i\neq j} 1 
= 3 N_y\sigma^4 + \sigma^4 (N_y^2 - N_y) \\
 &amp;=&amp; 2 N_y \sigma^4 + N_y^2 \sigma^4, \\
\end{eqnarray}\]

<p>where we have used the facts that:</p>

<ul>
  <li>the expected value of the product of statistically independent random variables
is the product of the expected values,</li>
  <li>the second <a href="https://en.wikipedia.org/wiki/Normal_distribution#Moments">central moment</a>
of a Gaussian is its variance \(E[(y_j-f_j(\boldsymbol{p})^2]=\sigma_j^2=w_j^2\sigma^2\).</li>
  <li>the fourth central moment is \(E[(y_j-f_j(\boldsymbol{p})^4]=3\sigma_j^2=3 w_j^2\sigma^2\)</li>
</ul>

<p>We also use this to calculate the next term in the sum for \(I_{\sigma\sigma}\):</p>

\[\begin{eqnarray}
E\left[\sum_j \frac{(y_j-f_j(\boldsymbol{p}))^2}{w_j^2}\right] &amp;=&amp;\sum_j \frac{E[(y_j-f_j(\boldsymbol{p}))^2]}{w_j^2} \\ 
&amp;=&amp; \sum_j \frac{w_j^2\sigma^2}{w_j^2} = N_y \sigma^2.\\ 
\end{eqnarray}\]

<p>Since \(E[1]=1\), we can put everyting togeter to obtain</p>

\[I_{\sigma\sigma}=\frac{2 N_y+N_y^2-2N_y^2+N_y^2}{\sigma^2}=\frac{2 N_y}{\sigma^2}.\]

<h2 id="b2-calculating-v_ps_k1">B.2 Calculating \([v_{PS}]_{k1}\)</h2>

<p>The elements of the column vector \(\boldsymbol{v}_{PS}\) are calculated as:</p>

\[\begin{eqnarray}
[\boldsymbol{v}_{PS}]_k &amp;=&amp; E\left[ \left(-\frac{N_y}{\sigma}+\frac{1}{\sigma^3}\sum_j \frac{(y_j-f_j(\boldsymbol{p}))^2}{w_j^2} \right)\cdot \left(\frac{1}{\sigma^2}\sum_j \frac{y_j-f_j(\boldsymbol{p})}{w_j^2}\cdot\frac{\partial f_j}{\partial p_k}(\boldsymbol{p}) \right) \right] \\
 &amp;=&amp; -\frac{N_y}{\sigma^3}\sum_j \frac{E[y_j-f_j(\boldsymbol{p})]}{w_j^2}\cdot\frac{\partial f_j}{\partial p_k}(\boldsymbol{p}) + \frac{1}{\sigma^5}\sum_{i,j} \frac{E[(y_j-f_j(\boldsymbol{p}))^2 \cdot (y_i-f_i)]}{w_i^2 w_j^2} \\
 &amp;=&amp; 0 + \frac{1}{\sigma^5}\sum_{i,j;i=j} \frac{E[(y_j-f_j(\boldsymbol{p}))^2 \cdot (y_i-f_i)]}{w_i^2 w_j^2} + \frac{1}{\sigma^5}\sum_{i,j;i\neq j} \frac{E[(y_j-f_j(\boldsymbol{p}))^2 \cdot (y_i-f_i)]}{w_i^2 w_j^2} \\
 &amp;=&amp; 0 + \frac{1}{\sigma^5}\sum_{j} \frac{E[(y_j-f_j(\boldsymbol{p}))^3]}{w_i^2 w_j^2} + \frac{1}{\sigma^5}\sum_{i,j;i\neq j} \frac{E[(y_j-f_j(\boldsymbol{p}))^2 \cdot (y_i-f_i)]}{w_i^2 w_j^2} \\
 &amp;=&amp; 0 + 0 + \frac{1}{\sigma^5}\sum_{i,j;i\neq j} \frac{E[(y_j-f_j(\boldsymbol{p}))^2 \cdot (y_i-f_i)]}{w_i^2 w_j^2} \\
 &amp;=&amp; 0 + 0 + \frac{1}{\sigma^5}\sum_{i,j;i\neq j} \frac{E[(y_j-f_j(\boldsymbol{p}))^2] \cdot E[(y_i-f_i)]}{w_i^2 w_j^2} \\
 &amp;=&amp; 0 + 0 + 0 = 0,
\end{eqnarray}\]

<p>where we again have used the rule about the expected value of independent
random variables and the facts that \(E[y_j-f_j(\boldsymbol{p})]=0\),
and the third central moment of a Gaussian is \(E[(y_j-f_j(\boldsymbol{p}))^3]=0\).</p>

<h2 id="b3-calculating-i_pp_kl">B.3 Calculating \([I_{PP}]_{kl}\)</h2>

<p>Now for the final piece of the Fisher information matrix:</p>

\[\begin{eqnarray}
[\boldsymbol{I}_{PP}(\boldsymbol{p},\sigma)]_{kl} &amp;=&amp; E\left[\left(\frac{1}{\sigma^2}\sum_j \frac{y_j-f_j(\boldsymbol{p})}{w_j^2}\frac{\partial f_j(\boldsymbol{p})}{\partial p_k}\right)\cdot\left(\frac{1}{\sigma^2}\sum_i \frac{y_i-f_i(\boldsymbol{p})}{w_j^2}\frac{\partial f_i(\boldsymbol{p})}{\partial p_l}\right)\right] \\
 &amp;=&amp; \frac{1}{\sigma^4} \sum_{i,j} \frac{\partial f_j(\boldsymbol{p})}{\partial p_k}\frac{\partial f_i(\boldsymbol{p})}{\partial p_l} \frac{1}{w_i^2 w_j^2} E[(y_j-f_j(\boldsymbol{p}))(y_i-f_i(\boldsymbol{p}))]
\end{eqnarray}\]

<p>We recognize the \(E[(y_j-f_j(\boldsymbol{p}))(y_i-f_i(\boldsymbol{p}))]=\sigma_{ij}\) as the
covariance of \(y_i\), \(y_j\). Since we assume statistical independence, we can
write this as</p>

\[\sigma_{ij} = \left\{
\begin{matrix} \sigma_j^2 &amp;; j = i \\
               0 &amp;; j \neq i .\\
\end{matrix} \right.\]

<p>And since we know \(\sigma_j = w_j \sigma\), it follows that</p>

\[\begin{eqnarray}
[\boldsymbol{I}_{PP}(\boldsymbol{p},\sigma)]_{kl} &amp;=&amp; \frac{1}{\sigma^2} \sum_{j} \frac{\partial f_j(\boldsymbol{p})}{\partial p_k}\frac{1}{w_j^2} \frac{\partial f_i(\boldsymbol{p})}{\partial p_l} \\
 &amp;=&amp; \frac{1}{\sigma^2} \left[ \boldsymbol{J}_f^T(\boldsymbol{p}) \boldsymbol{W}^T \boldsymbol{W} \boldsymbol{J}_f(\boldsymbol{p}) \right]_{kl}
\end{eqnarray}\]

<p>where \(\boldsymbol{J}_f\) is the Jacobian of \(f\) and \(\boldsymbol{W}\) is the
weight matrix as defined in eq. \(\eqref{relative-weights-matrix}\).</p>

<h2 id="jeffreys-prior-from-the-fisher-information-matrix">Jeffreys’ Prior from the Fisher Information Matrix</h2>

<p>Now, putting it all together, our Fisher information matrix is a block-diagonal
matrix like this:</p>

\[\boldsymbol{I}(\boldsymbol{p},\sigma) =
\frac{1}{\sigma^2}
\left(
\begin{matrix}
 \boldsymbol{J}_f^T(\boldsymbol{p}) \boldsymbol{W}^T \boldsymbol{W} \boldsymbol{J}_f(\boldsymbol{p}) &amp; \boldsymbol{0} \\
 \boldsymbol{0} &amp;  2 N_y\\
\end{matrix}
\right)\]

<p>Jeffreys’ prior is calculated as the square root of the determinant of the Fisher
information matrix. Using the rules for the determinants of <a href="https://en.wikipedia.org/wiki/Determinant#Immediate_consequences">scaled matrices</a>
and for <a href="https://en.wikipedia.org/wiki/Determinant#Block_matrices">block diagonal matrices</a>,
we can calculate it as:</p>

\[\begin{eqnarray}
P(\boldsymbol{p},\sigma) &amp;\propto&amp; \sqrt{\text{det}\boldsymbol{I}(\boldsymbol{p},\sigma)}\\
 &amp;\propto&amp; \frac{1}{\sigma^{N_p+1}} \sqrt{\text{det}\boldsymbol{J}_f^T(\boldsymbol{p}) \boldsymbol{W}^T \boldsymbol{W} \boldsymbol{J}_f(\boldsymbol{p})},
\end{eqnarray}\]

<p>where we have dropped all factors that do not depend on \(\boldsymbol{p}\) or \(\sigma\).</p>

<h1 id="endnotes">Endnotes</h1>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:exercise-reader" role="doc-endnote">
      <p>Finally, I have an opportunity to be the one writing that sentence. <a href="#fnref:exercise-reader" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:uniform-prior" role="doc-endnote">
      <p>Some problems arise when thinking in depth about the meaning of uniform priors. Those don’t have a lot of practical importance, but are interesting nonetheless. They are discussed e.g. in Sivia’s brilliant <a href="https://global.oup.com/academic/product/data-analysis-9780198568322https://global.oup.com/academic/product/data-analysis-9780198568322">Data Analysis - A Bayesian Tutorial</a>. <a href="#fnref:uniform-prior" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:ignorance" role="doc-endnote">
      <p>Priors conveying ignorance are actually surprisingly tricky. We’ll see another one of those that has a vastly different functional form later. <a href="#fnref:ignorance" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:covpar" role="doc-endnote">
      <p>Since the variances of the parameters are on the diagonal of the covariance matrix. <a href="#fnref:covpar" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:approx-f" role="doc-endnote">
      <p>Maybe there is an argument here, if we argue that \(P(\boldsymbol{p}\vert \boldsymbol{y})\) is reasonably sharply peaked around \(\boldsymbol{p}^\dagger\), but I’m not sure. <a href="#fnref:approx-f" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:prop-uncertainty" role="doc-endnote">
      <p>The formula for propagation of uncertainty is also derived under the approximation of linearity of the transformation. <a href="#fnref:prop-uncertainty" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="least-squares" /><category term="algorithm" /><category term="math" /><category term="bayesian" /><summary type="html"><![CDATA[In this article, we’ll derive from scratch the well known formulas –and some not so well known ones– for nonlinear least squares fitting from a Bayesian perspective. We’ll be using only elementary linear algebra and elementary calculus. It turns out, that this is a valuable exercise, because it allows us to clearly state our assumptions about the problem and assign unambigous meaning to all components of the least squares fitting process.]]></summary></entry><entry><title type="html">Using Address Sanitizer for a C or C++ Library Linked to a Rust Executable</title><link href="https://geo-ant.github.io/blog/2024/rust-address-sanitizer-with-c/" rel="alternate" type="text/html" title="Using Address Sanitizer for a C or C++ Library Linked to a Rust Executable" /><published>2024-07-12T00:00:00+00:00</published><updated>2024-07-12T00:00:00+00:00</updated><id>https://geo-ant.github.io/blog/2024/rust-address-sanitizer-with-c</id><content type="html" xml:base="https://geo-ant.github.io/blog/2024/rust-address-sanitizer-with-c/"><![CDATA[<p>When using Rust, it’s easy to forget about segfaults, buffer overflows, and related
memory safety bugs. Recently, I had a problem when I linked a C++ library to my Rust executable
and said library was producing segfaults (among other things). The way I decided to tackle the
problem, was to use Address Sanitizer (ASan). I’ll summarize the procedure here in case anyone
else runs into the same problem and they might find this useful.</p>

<h1 id="scenario">Scenario</h1>

<p>Our scenario is this: we have a C library whose functions we invoke from a Rust
executable via Rust’s FFI. This example will work just as well if the library
is written in C++ and exposes its functions via <code class="language-plaintext highlighter-rouge">extern "C"</code> linkage.</p>

<p>Let’s assume that we have the source code of the C or C++ library and we are able
to rebuild it. Here, we’ll use CMake for building, but the specific build system is
not important, as long as we can pass the correct compiler and linker flags.
Other options are available, this workflow has just
become a preference for me personally. Finally, I also assume that we are running on x86 Linux,
since I am not sure what the status of address sanitizer is on Windows and I am too
lazy to find out.</p>

<h1 id="the-c-library-code">The C Library Code</h1>

<p>The code in this article contains everything you need to run this example. Thus,
we’ll use some silly code for both the C and the Rust part, just enough to trigger
problems that we can examine with ASan.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// lib.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span>
<span class="c1">// "private", only used internally</span>
<span class="k">static</span> <span class="kt">uint32_t</span> <span class="nf">access_internal</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="n">pointer</span><span class="p">,</span>
                                <span class="kt">size_t</span> <span class="n">index</span><span class="p">);</span>

<span class="c1">// the "public" function</span>
<span class="kt">uint32_t</span> <span class="nf">allocate_and_access</span><span class="p">(</span><span class="kt">size_t</span> <span class="k">const</span> <span class="n">size</span><span class="p">,</span>
                             <span class="kt">size_t</span> <span class="k">const</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
  <span class="kt">uint32_t</span><span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">calloc</span><span class="p">(</span><span class="n">size</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">uint32_t</span><span class="p">));</span>
  <span class="kt">uint32_t</span> <span class="k">const</span> <span class="n">value</span> <span class="o">=</span> <span class="n">access_internal</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">index</span><span class="p">);</span>
  <span class="n">free</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">uint32_t</span> <span class="nf">access_internal</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="k">const</span> <span class="n">pointer</span><span class="p">,</span>
                                <span class="kt">size_t</span> <span class="k">const</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="n">pointer</span><span class="p">[</span><span class="n">index</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>

<p>When compiled as a shared library, it will export the function <code class="language-plaintext highlighter-rouge">allocate_and_access</code>,
which allocates <code class="language-plaintext highlighter-rouge">size</code> number of <code class="language-plaintext highlighter-rouge">u32</code> integers on the heap and returns the
value at index <code class="language-plaintext highlighter-rouge">index</code>. If the index is within the range <code class="language-plaintext highlighter-rouge">[0,...,size-1]</code>, the
function will return the value <code class="language-plaintext highlighter-rouge">0</code>, because <code class="language-plaintext highlighter-rouge">calloc</code> initializes the
memory to zero. If <code class="language-plaintext highlighter-rouge">index</code> is outside of the range, that’s an out of bounds memory access,
which produces undefined behavior. I have used an internal “private”<sup id="fnref:static" role="doc-noteref"><a href="#fn:static" class="footnote" rel="footnote">1</a></sup> function
that performs the actual access, just so that Address Sanitizer has more of a 
stack trace to report.</p>

<p>Before we see how to build the library with Address Sanitizer so that it plays
nicely with a Rust executale, let’s take a look at the overall project structure and
the Rust code.</p>

<h1 id="the-rust-project">The Rust Project</h1>

<p>Our simple project is structured as follows:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
|- build.rs
|- Cargo.toml
|- myclib
|   |- CMakeLists.txt
|   |- lib.c
|- src
    |- main.rs
</code></pre></div></div>

<p>It’s just a bog standard Rust executable project that contains our C library
in a subdirectory. Our Rust executable calls the C function via the
<a href="https://doc.rust-lang.org/rust-by-example/std_misc/ffi.html">foreign function interface</a>
and prints the result.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.rs</span>
<span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">allocate_and_access</span><span class="p">(</span><span class="n">size</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span> <span class="n">index</span><span class="p">:</span> <span class="nb">usize</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">u32</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"Calling C function..."</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">retval</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">allocate_and_access</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">)</span> <span class="p">};</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"...returned: {}"</span><span class="p">,</span> <span class="n">retval</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can see that the program will perform an out of bounds access when run like that.
In this case, it will likely print some random nonsense<sup id="fnref:ub" role="doc-noteref"><a href="#fn:ub" class="footnote" rel="footnote">2</a></sup>. For completeness, let’s
see what the <code class="language-plaintext highlighter-rouge">build.rs</code> file looks like, before we get into how to debug the program
with ASan.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//build.rs</span>
<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">dst</span> <span class="o">=</span> <span class="nn">cmake</span><span class="p">::</span><span class="nf">build</span><span class="p">(</span><span class="s">"myclib"</span><span class="p">);</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"cargo:rustc-link-search=native={}/lib"</span><span class="p">,</span> <span class="n">dst</span><span class="nf">.display</span><span class="p">());</span>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"cargo:rustc-link-lib=dylib=myclib"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This has a build-dependency on the <a href="https://crates.io/crates/cmake">cmake</a> crate, just
because that is how I like to build my C and C++ projects. Let’s now look at the CMake
file for the C project, because that is where we pass the necessary
compiler and linker flags to build our library with Address Sanitizer enabled.</p>

<h1 id="building-our-c-project-with-static-address-sanitizer">Building Our C Project With (Static) Address Sanitizer</h1>

<p>Enabling Address Sanitizer for our library is as easy as passing the correct
compiler and linker flags. However, if we just pass <code class="language-plaintext highlighter-rouge">-fsanitize=address</code>, we will
run into <a href="https://github.com/rust-lang/rust/issues/114127">this error</a> when executing our program:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">==</span><span class="nv">82818</span><span class="o">==</span>Your application is linked against incompatible ASan runtimes.
</code></pre></div></div>

<p>That’s because, at the time of writing, rustc only bundles the static version
of the Address Sanitizer runtime. Thus, to use our C library with our Rust executable,
we have to make sure that we also link the library with the <em>static</em> version
of the Address Sanitizer runtime. Here is how that looks in our simple <code class="language-plaintext highlighter-rouge">CMakeLists.txt</code> file:</p>

<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># CMakeLists.txt</span>
<span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.10<span class="p">)</span>
<span class="nb">project</span><span class="p">(</span>MyCProject C<span class="p">)</span>
<span class="nb">add_library</span><span class="p">(</span>myclib SHARED lib.c<span class="p">)</span>

<span class="c1"># the flags to use ASan statically</span>
<span class="nb">target_compile_options</span><span class="p">(</span>myclib PUBLIC 
 -fsanitize=address<span class="p">)</span>
<span class="nb">target_link_options</span><span class="p">(</span>myclib PUBLIC
 -fsanitize=address 
 -static-libasan<span class="p">)</span>

<span class="c1"># needed for the cmake crate</span>
<span class="nb">install</span><span class="p">(</span>TARGETS myclib
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib<span class="p">)</span>
</code></pre></div></div>

<p>We have now made sure that the static runtime of ASan gets linked to our library.</p>

<h1 id="running-the-executable-with-address-sanitizer">Running the Executable With Address Sanitizer</h1>

<p>At the time of writing, we need the <em>nightly</em> rust compiler to use Address Sanitizer
in our programs<sup id="fnref:stabilize" role="doc-noteref"><a href="#fn:stabilize" class="footnote" rel="footnote">3</a></sup>. So we have to make sure that our project uses the nightly
toolchain, if it’s not already doing that:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rustup override <span class="nb">set </span>nightly
</code></pre></div></div>
<p>This will make sure that the given project, and <em>only that</em>, uses the nightly
compiler, which allows us to pass the <code class="language-plaintext highlighter-rouge">-Z</code> family of flags. To run our program,
we essentially just <code class="language-plaintext highlighter-rouge">cargo run</code> it with some extra compiler flags:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ RUSTFLAGS</span><span class="o">=</span><span class="s2">"-Z sanitizer=address"</span> cargo run <span class="nt">--target</span> x86_64-unknown-linux-gnu
</code></pre></div></div>

<p>Note that we also have to specify the target explicitly<sup id="fnref:target" role="doc-noteref"><a href="#fn:target" class="footnote" rel="footnote">4</a></sup>.</p>

<h1 id="analyzing-the-address-sanitizer-output">Analyzing the Address Sanitizer Output</h1>

<p>We’ll now have a quick look at how to analyze the output of Address Sanitizer,
without going into great detail, because there are tons of tutorials on the web. 
But let’s at least see how we can make use of the output. When we run our program
we get an output such as this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Calling C function...
=================================================================
==10654==ERROR: AddressSanitizer: heap-buffer-overflow on address [...]
READ of size 4 at 0x50400000003c thread T0
    #0 0x78d074480247  (./target/debug/build/[...]/out/lib/libmyclib.so+0x1247) [...]
    #1 0x78d0744801d4  (./target/debug/build/[...]/out/lib/libmyclib.so+0x11d4) [...]
    #2 0x63b647f83a64  (./target/debug/asan-rust+0xeea64) (BuildId: c547f75d1a8b7697)
    #3 0x63b647f83e1a  (./target/debug/asan-rust+0xeee1a) (BuildId: c547f75d1a8b7697)
    #4 0x63b647f83c3d  (./target/debug/asan-rust+0xeec3d) (BuildId: c547f75d1a8b7697)
    #5 0x63b647f84084  (./target/debug/asan-rust+0xef084) (BuildId: c547f75d1a8b7697)
[...]
</code></pre></div></div>

<p>There’s more useful output, but this truncated version should suffice. ASan tells
us that we have a buffer-overflow as expected. It also gives us a stack trace,
which can be very helpful in debugging how that buffer overflow was actually
triggered. The top of the stack trace shows us where the overflow happened.
There’s just one problem: ASan just gives us raw offsets in our
binaries, such as <code class="language-plaintext highlighter-rouge">libmyclib.so+0x1247</code>. That means “the code at offset
<code class="language-plaintext highlighter-rouge">0x1247</code> of <code class="language-plaintext highlighter-rouge">libmyclib</code>”, which is still not very human readable.
There are a couple of things we can do to about that. Let’s see some of them.</p>

<h2 id="using-addr2line">Using addr2line</h2>
<p>We can use the GNU <a href="https://linux.die.net/man/1/addr2line">addr2line</a>
tool to convert addresses in binaries into lines of source code. This, and all the other
things I’ll mention below, requires that our library be compiled with debug
symbols enabled. We have implicitly done that because CMake defaults to the
debug build type, unless otherwise specified.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>addr2line <span class="nt">-f</span> <span class="nt">-p</span> <span class="nt">-e</span> ./target/debug/build/[...]/out/lib/libmyclib.so 0x1247
access_internal at ./myclib/lib.c:20
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">addr2line</code> tool tells us the function name and the line in the source
code that produced the buffer overflow. Not surprisingly, this is exactly the line
<code class="language-plaintext highlighter-rouge">return pointer[index];</code> in the <code class="language-plaintext highlighter-rouge">access_internal</code> function. Doing it like that
surely works, but it can become tedious quickly.</p>

<h2 id="using-the-symbolizer">Using the Symbolizer</h2>

<p>If we have <a href="https://apt.llvm.org/">llvm installed</a>, there is a tool called the
<code class="language-plaintext highlighter-rouge">llvm-symbolizer</code>. It might not be called exactly like that, e.g. for my particular installation
it’s called <code class="language-plaintext highlighter-rouge">llvm-symbolizer-18</code>. We can tell ASan about it by using a dedicated environment
variable. Then, we use another dedicated environment variable to instruct ASan to
use it to prettify its output.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">ASAN_SYMBOLIZER_PATH</span><span class="o">=</span><span class="si">$(</span>which llvm-symbolizer-18<span class="si">)</span>
<span class="nv">$ </span><span class="nb">export </span><span class="nv">ASAN_OPTIONS</span><span class="o">=</span><span class="nv">symbolize</span><span class="o">=</span>1
</code></pre></div></div>

<p>If we now run our program as above, the output is much easier to grasp:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Calling C function...
=================================================================
==13863==ERROR: AddressSanitizer: heap-buffer-overflow on address [...]
READ of size 4 at 0x50400000003c thread T0
    #0 0x70e4a4ca2247 in access_internal ./myclib/lib.c:20:17
    #1 0x70e4a4ca21d4 in allocate_and_access ./myclib/lib.c:12:26
    #2 0x5b2614b25d74 in asan_rust::main::h67804a126392dee0 ./src/main.rs:7:27
[...]
</code></pre></div></div>

<p>This was exactly the information that I needed to fix my particular problem. If the output of ASan
is particularly unwieldy, we can also <a href="https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags">direct it into a file</a>
using the <code class="language-plaintext highlighter-rouge">ASAN_OPTIONS</code> environment variable, or via piping the <code class="language-plaintext highlighter-rouge">stderr</code> output
to a file.</p>

<h1 id="conclusion">Conclusion</h1>

<p>ASan proved invaluable for me, because it helped me find and eventually fix a weird
out of bounds memory access, that was producing segfaults sometimes and hot
garbage at other times. I was really happy that the integration across languages
was pretty smooth, after figuring out I needed the static runtime. There is much
more we can do with ASan in Rust, for example, it can also help us find some problems
in unsafe Rust code, a small –but important– subset of what <a href="https://github.com/rust-lang/miri">miri</a> does,
where ASan lets the program run much faster than miri.</p>

<h1 id="further-reading">Further Reading</h1>

<ul>
  <li><a href="https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html">Rust documentation</a> for sanitizers.</li>
  <li><a href="https://github.com/google/sanitizers/wiki/AddressSanitizer">Google’s documentation</a> for Address Sanitizer.</li>
  <li><a href="https://github.com/rust-lang/rust/pull/123617">Stabilization PR</a> for AddressSanitizer and LeakSanitizer in rustc.</li>
</ul>

<h1 id="endnotes">Endnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:static" role="doc-endnote">
      <p>The correct term is <em>static</em> or <em>internal</em> linkage. In effect, the function cannot be called from outside the library (or even outside of this particular C File… <em>compilation unit</em>… damn you voice in my head!). <a href="#fnref:static" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:ub" role="doc-endnote">
      <p>But we cannot rely on that. Accessing an invalid pointer is <em>undefined behavior</em>, which can mean any number of things. <a href="#fnref:ub" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:stabilize" role="doc-endnote">
      <p>Sanitizers <a href="https://github.com/rust-fuzz/cargo-fuzz/pull/376">may soon be stabilized</a>, so make sure to check. See also <a href="https://github.com/rust-lang/rust/pull/123617">this stabilization PR</a>. Thanks to u/Shnatsel for <a href="https://www.reddit.com/r/rust/comments/1e1tkpz/comment/lcwkw0g/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">pointing that out</a>. <a href="#fnref:stabilize" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:target" role="doc-endnote">
      <p>Though I have also seen it work without the explicit target, but better safe than sorry. <a href="#fnref:target" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="c++" /><category term="c" /><category term="sanitizers" /><summary type="html"><![CDATA[When using Rust, it’s easy to forget about segfaults, buffer overflows, and related memory safety bugs. Recently, I had a problem when I linked a C++ library to my Rust executable and said library was producing segfaults (among other things). The way I decided to tackle the problem, was to use Address Sanitizer (ASan). I’ll summarize the procedure here in case anyone else runs into the same problem and they might find this useful.]]></summary></entry></feed>