<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Kolen Cheung</title>
<link>https://blog.kolen.dev/</link>
<atom:link href="https://blog.kolen.dev/index.xml" rel="self" type="application/rss+xml"/>
<description>Research Software Engineer — physics, computing, reproducibility</description>
<generator>quarto-1.9.37</generator>
<lastBuildDate>Wed, 05 Aug 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>A Primer on Just-In-Time (JIT) Compilation</title>
  <dc:creator>Dr. Kolen Cheung, Research Software Engineer</dc:creator>
  <link>https://blog.kolen.dev/RSE/UoE/2026-08-05-jit-article.html</link>
  <description><![CDATA[ 





<!-- Everything before the first heading is article-only. Keep this comment
INSIDE the div: a raw HTML block out here survives into revealjs and pandoc
turns it into a stray empty first slide. -->
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><embed src="./2026-08-05-jit.html" style="width:100.0%" height="400"></p>
<figcaption><a href="./2026-08-05-jit.html">Slide to the talk</a></figcaption>
</figure>
</div>
<p>This is a companion write-up to a talk I gave at the RSA technical catch-up on 5 August 2026. A while ago I ran a poll for the journal club and this was the topic that came out on top, so here it is — as a technical catch-up rather than a journal club, since there is no obvious paper to read.</p>
<p>I have talked about two of the three tools here before; see the <a href="../PyAutoLens/2025-09-21-autojax-article.html">Numba vs.&nbsp;JAX case study</a> from PyCon UK 2025. Julia is the new one, carried in from a project we recently finished: a just-in-time compiled language dedicated to scientific computing.</p>
<p>The slides and this write-up come off a single source, so everything that was on screen is below. The prose around it is what I said on the day, cleaned up — and where I ran out of time, which was most of the middle section and nearly all of the last one, it is what I would have said, written from the slides. Every code sample has a runnable notebook behind it, linked at the top of its section.</p>
<section id="before-we-start" class="level1">
<h1>Before we start</h1>
<div class="notes">
<p>Two things before the tools themselves: the question everything below hangs off, and a list of the JIT compilers already running on your machine.</p>
</div>
<section id="which-language-am-i-actually-writing" class="level2">
<h2 class="anchored" data-anchor-id="which-language-am-i-actually-writing">Which language am I actually writing?</h2>
<div class="slide-subtitle">
<p>Not the one in the file extension. Who should have written it: me, or the compiler?</p>
</div>
<div class="notes">
<p>The answer is never the one in the file extension. Every speed-up below is a rewrite that computes exactly the same thing as the version before it — same language, same file, same result — which makes it a compiler pass, performed by hand, in the source layer, because that was the only layer I was allowed to stand on. So the question is really: who should have written it, me or the compiler?</p>
<p>Put plainly: the different optimisations I show below within a single language are not performance tips. They are the demonstration that <strong>the abstraction is leaking</strong>: you are standing in for the compiler, transforming one function into another that computes the same thing and runs at a different speed, because the compiler sees the two differently.</p>
<p>Seen that way, Julia and JAX are powerful for opposite reasons. Julia’s metaprogramming lets you program the compiler, so instead of transforming the code by hand you build the abstraction that lets the compiler do the transforming. JAX goes the other way: it is restrictive enough that hand-transforming is not on the table, so you write it idiomatically and the compiler does the rest. Neither is free — Julia still needs you to find the transformation first, and JAX’s restriction only pays inside the domain it covers — but Numba does neither, which is why it is where the leak shows most plainly.</p>
<p>Numba, JAX and Julia are three different answers to that — not three speeds. Each comes with its own tower of abstractions, its own restricted dialect, and its own idea of what you are allowed to say, and the optimisation strategy arrives bundled with the rest rather than being something you pick separately.</p>
<p>So: one long section of concrete examples, three JIT compilers taken one at a time; then a step back into the more abstract way of talking about them; then, at the end, the surprising bit.</p>
</div>
</section>
<section id="you-already-run-a-dozen-jits" class="level2">
<h2 class="anchored" data-anchor-id="you-already-run-a-dozen-jits">You already run a dozen JITs</h2>
<ul>
<li><strong>Browsers.</strong> Practically every JS engine in use is a JIT.</li>
<li><strong>Databases.</strong> PostgreSQL (<code>jit=on</code>, LLVM-based, since PG11); Spark whole-stage codegen.</li>
<li><strong>CUDA itself.</strong> PTX → SASS at load time — which is why the first kernel launch is slow, and why <code>~/.nv/ComputeCache</code> exists.</li>
<li><strong><code>torch.compile</code></strong> — Dynamo rewrites bytecode → Inductor → Triton. Ubiquitous in ML; it arrives with PyTorch whether or not you went looking for a JIT.</li>
<li><strong>The Linux kernel.</strong> eBPF is JIT-compiled.</li>
<li><strong>Your GPU driver</strong>, every time it compiles a shader.</li>
<li><strong>Regex.</strong> PCRE2-JIT. <strong>CPython itself</strong>, since 3.13 (PEP 744, experimental, opt-in).</li>
</ul>
<p>This is not an exotic topic you can opt out of.</p>
<div class="notes">
<p>CPython’s own is the only one on that list you have to opt into — <a href="https://peps.python.org/pep-0744/">PEP 744</a>, still experimental and off by default. Everything else is running whether you picked it or not.</p>
</div>
</section>
</section>
<section id="introducing-numba" class="level1">
<h1>Introducing Numba</h1>
<div class="notes">
<p><a href="https://numba.readthedocs.io/en/stable/">Numba</a> is a Python library. Once you import it you get a decorator, and that decorator completely hijacks the function it is applied to and turns it into something else: it compiles it.</p>
</div>
<section id="a-decorator-and-a-tower-underneath-it" class="level2">
<h2 class="anchored" data-anchor-id="a-decorator-and-a-tower-underneath-it">A decorator, and a tower underneath it</h2>
<div class="notes">
<p>Here is a very simple example: a one-line Python function doing some linear algebra, and the change you have to make to get it just-in-time compiled. It is not much of a change — you add a decorator. The signature I have written into it is unnecessary in this case; a bare <code>@jit</code> works here. I like being specific.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>Numba-jit can accelerate even a simple NumPy function that should already be quite fast — 1.87 μs → 791 ns here</li>
<li>The win is not “compiled beats interpreted”: it is <strong>operator fusion</strong>
<ul>
<li>memory allocation: 4 in NumPy — <code>A @ B</code>, <code>α · (A @ B)</code>, <code>β · C</code>, and the sum</li>
<li>2 in Numba — <code>A @ B</code> and the fused result</li>
</ul></li>
<li>tower of abstractions: CPython bytecode → Numba IR → typed Numba IR → LLVM IR → assembly</li>
<li>Numba compiles from <strong>bytecode</strong>, not source — the decorator is the seam between Python-as-host-language and Python-as-mini-language</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison/notebook/jit_comparison/intro_numba.html"><code>intro_numba.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mul_numpy(C, A, B, α<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, β<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb1-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (A <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> B) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> C</span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jit</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f8[:, ::1](f8[:, ::1], f8[:, ::1], f8[:, ::1], f8, f8)"</span>, nopython<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, nogil<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mul_numba(C, A, B, α<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, β<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb1-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (A <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> B) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> C</span></code></pre></div></div>
<pre><code>mul_numpy   1.87 μs
mul_numba    791 ns      2.4×

new arrays created:  numpy 4,  numba 2</code></pre>
</div>
</div>
<div class="notes">
<p>The compiled version is about 2.4× faster than the pure NumPy one, and the reason is where the abstraction falls. In Python every operation in that expression goes through an operator, and every operator creates a new object. <code>A @ B</code> multiplies the two and hands you back a new array. <code>α *</code> that gives you another new array. <code>β * C</code> gives you a third. The addition itself gives you a fourth. You need one thing at the end, and you have gone through a lot of memory allocation to get there. Put the decorator on and Numba is able to see through most of it and cut the allocations down.</p>
<p>The one it cannot cut is the matrix multiplication. Numba still does two allocations here, and in principle you only need one. <code>A @ B</code> gets dispatched to a linear algebra library, which allocates its own output, and Numba cannot see past that operator to avoid it. (The notebook checks the 4 against 2 in three independent ways: <code>arrayexpr</code> nodes in the typed IR, <code>tracemalloc</code> peak bytes, and an <code>ndarray</code> subclass counting <code>__array_finalize__</code>.)</p>
<p>The other thing worth highlighting is that what happens behind the scenes is very complicated, even though all we did was add one decorator. What Numba sees is <strong>not your source code</strong>. CPython has already compiled the function to bytecode, so Numba sees the bytecode first, and it has to be able to understand that bytecode — in some cases manipulating it — to work out what you wrote. Then it keeps lowering: to Numba’s own IR, then to a typed Numba IR, then to LLVM IR, then to assembly. It exposes every rung on the way down, through <code>.inspect_types()</code>, <code>.inspect_llvm()</code> and <code>.inspect_asm()</code>, which is what the notebook walks through.</p>
</div>
</section>
<section id="why-write-the-loop-fusion-stops-where-you-named-a-value" class="level2">
<h2 class="anchored" data-anchor-id="why-write-the-loop-fusion-stops-where-you-named-a-value">Why write the loop? Fusion stops where you named a value</h2>
<div class="notes">
<p>This one is more complicated, and it is built to show two things: that in Numba you can write loops and still be quite fast, and that here writing the loop is <em>faster</em> — which is counter-intuitive to what we are trained to believe when we write array code. In NumPy you optimise by vectorising, by thinking about broadcasting and not using loops. The kernel is a Mexican hat over a grid.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<p><img src="https://latex.codecogs.com/png.latex?%5Cpsi_%5Csigma(x,%20y)%20=%20(1%20-%20r%5E2)%5C,%20e%5E%7B-r%5E2%20/%202%7D,%20%5Cqquad%20r%5E2%20=%20%5Cfrac%7Bx%5E2%20+%20y%5E2%7D%7B%5Csigma%5E2%7D"></p>
<ul>
<li><code>f_numpy</code> is apparently the fastest possible NumPy</li>
<li><code>f_numba</code> — same source, compiled — buys only <strong>1.16×</strong>: 16M points already amortised the interpreter, and this kernel is memory-bandwidth-bound</li>
<li>Numba’s fuser works one <code>arrayexpr</code> node at a time, over the array notation <em>as written</em> — naming <code>r2</code> forces it into memory: <strong>a 127 MB round trip</strong></li>
<li><code>f_numba_loops</code> is optimal: hoisted, one allocation, <code>r2</code> in a register — you gave up notation and bought the top of the memory hierarchy</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison/notebook/jit_comparison/numba_loop.html"><code>numba_loop.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> f_numpy(x, y, σ):</span>
<span id="cb3-2">    r2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (x.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> y.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (σ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> σ)</span>
<span id="cb3-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> r2) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.exp(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> r2)</span>
<span id="cb3-4"></span>
<span id="cb3-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jit</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f8[:, ::1](f8[::1], f8[::1], f8)"</span>, nopython<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, nogil<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb3-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> f_numba_loops(x, y, σ):</span>
<span id="cb3-7">    res <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.empty((x.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], y.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]))</span>
<span id="cb3-8">    inv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (σ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> σ)</span>
<span id="cb3-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(x.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]):</span>
<span id="cb3-10">        xi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> x[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> x[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> inv  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># invariant in j — hoisted out of it</span></span>
<span id="cb3-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> j <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(y.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]):</span>
<span id="cb3-12">            r2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> xi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> y[j] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> y[j] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> inv  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># a scalar, in a register</span></span>
<span id="cb3-13">            res[i, j] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> r2) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.exp(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> r2)</span>
<span id="cb3-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> res</span></code></pre></div></div>
<pre><code>                                                ms  vs np  peak allocs
f_numpy             broadcasting, interp.    50.26  1.00×   4.0      —
f_numba             same source, compiled    43.51  1.16×   2.0      2
f_numba_inlined     rewritten for fuser      46.67  1.08×   1.0      3
f_numba_loops       loops, hoisted           39.67  1.27×   1.0      1</code></pre>
</div>
</div>
<div class="notes">
<p>Adding <code>@jit</code> to the same source is only slightly faster, and none of these numbers move very far, because the ceiling here is memory bandwidth rather than arithmetic. The column to read is <strong>peak</strong>, not the time: 4 → 2 → 1 is the structural result, and the two ends of the table follow it. The middle row does not — <code>f_numba_inlined</code> halves the peak and is still slower than the plain <code>@jit</code> — because peak and allocation count answer different questions. It trades the 127 MB <code>r2</code> for two kilobyte-sized temporaries, so it holds half the memory while allocating three arrays instead of two. Only <code>f_numba_loops</code> wins on both counts.</p>
<p><code>f_numba_loops</code> is the most optimised version, and what it is doing is a lot of low-level optimisation. First, the loops are explicit. Rather than implicitly assuming everything is an array operation and letting broadcasting sort it out — both inputs are 1D, the result is 2D, so they have to be broadcast along different axes — you write down the <code>for i</code> and the <code>for j</code> yourself. Then there is hoisting: you spot a computation that would otherwise be done over and over again and you lift it out of the loop. The part involving <code>x</code> has nothing to do with the inner loop, so it goes above it, and that is one less computation per element. This is a very typical example of how you would optimise a function in C. You could write exactly the same algorithm in C and it would look very similar.</p>
<p>Now, why does the naive <code>@jit</code> version still allocate twice? Both of those lines — <code>r2 = ...</code> and the return — are individually understood by Numba as array expressions. What it cannot see past is fusing the two together. <code>r2</code> gets its own memory allocated, and then that <code>r2</code> is used in the second line for the next computation. Ideally you want the intermediate gone, and in the loop version it is; but the compiler, looking at the first function, does not see past the array expression. So <code>r2</code> becomes a 3600×4400 array, and that is a 127 MB round trip through memory that buys you nothing.</p>
<p>Two questions came from the room here.</p>
<p><em>“Does that mean it can see into the exponential function?”</em> It stays a call — it is not going to be expanded and lowered and optimised further. But that is not what this is about. The point is whether it can see that all of this is an elementwise operation. Expand that second line and it is <code>for i, for j</code>, repeating the same expression and putting out an entry, every element independent of every other. That is what a compiler ought to be able to see and act on, and if it does, it knows it never needed to create the intermediate <code>r2</code> array at all. Written the way it is written, the compiler cannot understand that. Could you write a compiler that sees past it? Yes, actually — there is an example a couple of sections down. Just not inside the abstraction Numba has built.</p>
<p><em>“Is reducing memory traffic mainly where Numba’s optimisation lies, or are there other places?”</em> The way I would put it is that <strong>Numba is almost C, with some OpenMP-like directives on top.</strong> So you optimise a Numba function by thinking like a C programmer, which is what is happening here: you have to think about your memory access pattern, and interchanging the two loop orders changes the performance.</p>
<p>Not because Numba is imitating C, though. Both of them lower to native code for the CPU you are sitting on — Numba through LLVM IR — and at that level it is the memory hierarchy that decides, so any language that gets close to the metal wants the same treatment. What Numba adds on top is that you are not obliged to write everything that way: the array notation is still there, and you drop into loops only where it pays — which also means that, where it does pay, the work is yours.</p>
<p>The leak I described at the start, in its plainest form: <code>f_numpy</code> → <code>f_numba_loops</code>.</p>
</div>
</section>
<section id="objmode-jit-always-compiles-it-does-not-always-help" class="level2">
<h2 class="anchored" data-anchor-id="objmode-jit-always-compiles-it-does-not-always-help"><code>objmode</code>: <code>jit</code> always compiles; it does not always help</h2>
<div class="notes">
<p>The short version first. Numba has something called object mode, and what it means is that if you put <code>@jit</code> on any Python function it will work — but it may work in the way where the compiler says <em>I cannot optimise this, I am giving it back to the Python interpreter</em>.</p>
<p>That is the highlight, and it is the general shape of all three of these tools. You build a certain abstraction, which is a small subset of the Python language, and you say: this I understand, and it can be compiled to something very fast; that I do not understand, and it is not part of my abstraction, so I hand it back. The mechanism is a guard: fall off the guard and the more general thing — Python, in this case — handles it.</p>
<p>The longer version, which is what the slide shows. <a href="https://numba.readthedocs.io/en/stable/user/withobjmode.html"><code>objmode</code></a> is that boundary drawn by hand rather than inferred: a hole through the type system for one named expression, so the rest of the function can stay in <code>nopython</code> mode. The example is Kepler’s equation solved element by element with <code>scipy.optimize.brentq</code>, which Numba has no way to compile.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li><code>jit</code> will always work, but it will not always be faster</li>
<li>When you stay inside a subset of Python (i.e.&nbsp;Numba’s dialect), you compile to machine code (language spec → speculation/specialisation)</li>
<li>When you don’t (fall off the guard) → interpreted: always right, but slow</li>
<li><code>objmode</code> is a <strong>hole punched through the type system</strong>, drawn by hand — it lets the <em>rest</em> of the function keep <code>nopython=True</code></li>
<li>It buys correctness at the boundary, not speed: here the <code>brentq</code> call <em>was</em> the runtime, so compiling the loop around it changes nothing</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison/notebook/jit_comparison/numba_objmode.html"><code>numba_objmode.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> kepler_equation(E, M, e):</span>
<span id="cb5-2">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> E <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> e <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.sin(E) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> M</span>
<span id="cb5-3"></span>
<span id="cb5-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jit</span></span>
<span id="cb5-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> kepler(M, e):</span>
<span id="cb5-6">    E <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.empty_like(M)</span>
<span id="cb5-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(M.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]):</span>
<span id="cb5-8">        Mi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> M[i]</span>
<span id="cb5-9">        E[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> optimize.brentq(kepler_equation, Mi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.1</span>, Mi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.1</span>, args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(Mi, e))</span>
<span id="cb5-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> E</span>
<span id="cb5-11"></span>
<span id="cb5-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> kepler_objmode(M, e):</span>
<span id="cb5-13">    E <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.empty_like(M)</span>
<span id="cb5-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(M.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]):</span>
<span id="cb5-15">        Mi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> M[i]</span>
<span id="cb5-16">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> objmode(Ei<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"float64"</span>):</span>
<span id="cb5-17">            Ei <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> optimize.brentq(kepler_equation, Mi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.1</span>, Mi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.1</span>, args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>(Mi, e))</span>
<span id="cb5-18">        E[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Ei</span>
<span id="cb5-19">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> E</span></code></pre></div></div>
<pre><code>python:   12.63 ms
forceobj: 12.79 ms
objmode:  13.26 ms</code></pre>
</div>
</div>
<div class="notes">
<p>The three timings are the point, and the point is that nothing happened. What <code>objmode</code> buys is a boundary drawn where you can see it: <code>forceobj</code> gives up on the whole function at once, whereas this gives up on one named expression and says so in the source.</p>
</div>
</section>
</section>
<section id="introducing-jax" class="level1">
<h1>Introducing JAX</h1>
<div class="notes">
<p>That is a fast introduction to Numba, but there are three of these to get through. Same function as before, the same bit of linear algebra. The decorator looks slightly different, and what happens beneath it is very different.</p>
</div>
<section id="tracing-not-typing" class="level2">
<h2 class="anchored" data-anchor-id="tracing-not-typing">Tracing, not typing</h2>
<div class="notes">
<p>Numba is fundamentally type-based. Either you spell the types out by hand, as I did above, or you skip it and at call time it works out what the types are and compiles a version of the function specialised to them. <a href="https://docs.jax.dev/">JAX</a> is not like that: it is a <strong>tracing</strong> compiler, which in a certain sense is similar and in mechanism is quite different.</p>
<p>You do not specify a type. When you pass an array into the function, JAX substitutes a stand-in for it: a <code>Tracer</code>, which carries no data at all, only an abstract value — a <code>ShapedArray</code>, which is to say a shape and a dtype and nothing else. Then, because the <code>Tracer</code> has gone in, the function actually gets called and runs, just as it would on any other object. Drop a <code>print</code> in the body and you can watch it happen. The tracing compiler is observing exactly that: the <code>Tracer</code> goes through all the logic in the function, every array operation it meets gets recorded, and the recording is the program.</p>
<p>So it is even more abstract than Numba. It does not see your source code, it does not even see your bytecode, it does not see how you wrote the logic — it traces the <em>effect</em> of the function. From there the representation goes to a jaxpr, then to StableHLO, then, depending on the architecture, in this case CPU, to LLVM IR and assembly. The windows onto it are <code>jax.make_jaxpr</code> and <code>.lower().compile().as_text()</code>, at the same altitude as Numba’s <code>inspect_types</code> and <code>inspect_llvm</code>.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>Numba type-specialises <strong>bytecode</strong>. JAX <strong>traces</strong>: it runs the function once on abstract stand-ins (<code>Tracer</code>s) and records every array op that fires — Python only <em>generates</em> the recording</li>
<li>tower of abstractions: tracing → jaxpr → StableHLO → fused HLO → LLVM IR (CPU) → assembly</li>
<li>Caches on <strong>abstract shape and dtype</strong> (a <code>ShapedArray</code>), not values — new shape, new trace, new compile</li>
<li><strong>JAX is not automatically faster.</strong> At <code>16×12×32</code> it <em>loses</em> to NumPy — that is per-call dispatch (pytree flatten → cache lookup → PjRt launch), not arithmetic. At <code>1024³</code> all three land within a factor of two, and which wins is run-dependent</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison/notebook/jit_comparison/intro_jax.html"><code>intro_jax.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jax.jit</span></span>
<span id="cb7-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> mul(C, A, B, α<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, β<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>):</span>
<span id="cb7-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (A <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> B) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> C</span></code></pre></div></div>
<pre><code>              16×12×32          1024³
  numpy        1.86 μs         4.56 ms
  numba         744 ns         2.49 ms
  jax          6.17 μs ←worst  2.79 ms</code></pre>
<p>XLA still finds the same fusion Numba’s <code>arrayexpr</code> optimiser found, from a completely different starting representation — <code>ROOT %multiply_add_fusion</code>. Fusion is not what is being measured here.</p>
</div>
</div>
<div class="notes">
<p>The interesting thing about JAX, which I have mentioned before, is that it specialises not only on type but also on shape. Feed a 1D float64 array of 1024 elements into a function, then one of 4096, and it will compile a new version for the second. That is where a lot of its advantage in accelerating a function comes from.</p>
<p>Not here, though. Compare this against what we had before and the JAX case is actually much slower — the numpy and numba figures are the same benchmark from a few slides ago, re-run, which is why they are close to those numbers without being identical. Change the array size and it comes out differently: at <code>1024³</code> I would not read “best” too hard, because it depends on the run and benchmarking is not that repeatable, but all three are in the same ballpark. This function is also simple enough that you cannot optimise it much further than it already is. In the <a href="../PyAutoLens/2025-09-21-autojax-article.html">PyCon UK talk</a> I showed plenty of cases where JAX <em>is</em> much faster — tens of percent, sometimes considerably more, precisely because it can see the shape too.</p>
<p>That prompted the right question from the room: so where is the overhead? Two places. First the tracing and compiling, which you pay on the first run. Second, because it is now dispatching on type <em>and</em> shape, there is a dispatch mechanism to go through: you arrive with some input, it has to find which compiled, specialised version of the function to run, and if that version does not exist yet it has to hand the work back to the compiler. So it is doing heavier dispatching than Numba’s thin typed wrapper, and it is built for a different regime — kernels big enough, or repeated often enough, to pay that back.</p>
</div>
</section>
<section id="write-the-maths-get-the-loop" class="level2">
<h2 class="anchored" data-anchor-id="write-the-maths-get-the-loop">Write the maths, get the loop</h2>
<div class="notes">
<p>Back to the Mexican hat, unchanged, this time given to JAX.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>Tracing erases the <em>name</em> <code>r2</code> before fusion ever runs — so XLA fuses straight through it, and picks fusion boundaries by cost rather than by which Python names appeared
<ul>
<li>the exact thing that cost Numba a 127 MB round trip</li>
</ul></li>
<li>Pure functional, fixed shape, static size: <strong>a tighter spec buys more aggressive optimisation</strong></li>
<li>No low-level control (c.f. Numba’s fastest version) — you cannot hand-write the loop</li>
<li>For simple algorithms, just write the maths: four lines get what Numba needed ten hand-written ones for</li>
<li>The cost moved, it did not vanish: retracing per shape, and no data-dependent control flow or mutation inside the compiled region</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison/notebook/jit_comparison/jax_loop.html"><code>jax_loop.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jax.jit</span></span>
<span id="cb9-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> f_jax(x, y, σ):</span>
<span id="cb9-3">    r2 <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (x.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> y.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (σ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> σ)</span>
<span id="cb9-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> r2) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> jnp.exp(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> r2)</span></code></pre></div></div>
<p>Two fusions for the whole kernel — one scalar, one grid:</p>
<pre><code>%multiply_divide_fusion      = f64[]           fusion(...)
%exponential_multiply_fusion = f64[3600,4400]  fusion(...)</code></pre>
<p>Both squares, both broadcasts, the add, the divide, <code>1 - r2</code>, <code>-0.5 * r2</code>, <code>exp</code>, the final multiply — every one of them, in a single pass. <code>r2</code> never exists as an array.</p>
</div>
</div>
<div class="notes">
<p>This is the compiler I said existed, the one that can see past a named intermediate. Tracing throws the <em>name</em> <code>r2</code> away before fusion ever runs, so by the time XLA gets the program there is no name left for fusion to stop at, and it picks its boundaries by cost instead. That is the 127 MB round trip, gone — for structural reasons, not because one fuser is better written than the other. A tighter specification buys more aggressive optimisation.</p>
<p>The price is everything Numba’s fastest version had. There is no JAX equivalent of <code>f_numba_loops</code> and no way to write one, so for a kernel this simple you get the good outcome for free — write the maths, and the compiler does what I had to do by hand — but the cost moved rather than vanished. You pay it in retracing on every new shape, and in what you are no longer allowed to write inside the compiled region: no data-dependent control flow, no mutation. Branch on a traced value and you get a <code>ConcretizationTypeError</code>, which is what <a href="./jit_comparison/notebook/jit_comparison/jax_fail.html"><code>jax_fail.ipynb</code></a> is about.</p>
</div>
</section>
<section id="numba-vs.-jax-on-a-real-kernel" class="level2">
<h2 class="anchored" data-anchor-id="numba-vs.-jax-on-a-real-kernel">Numba vs.&nbsp;JAX on a real kernel</h2>
<div class="notes">
<p>Everything above is a toy, built to make one point each. The one production kernel I have proper data for is the interferometer curvature calculation from PyAutoLens, which I benchmarked both ways for the PyCon UK talk.</p>
</div>
<ul>
<li>Same function, two implementations, from the <a href="../PyAutoLens/2025-09-21-autojax-article.html">Numba vs.&nbsp;JAX case study</a>: <code>w_tilde_curvature_interferometer_from</code> in <a href="../PyAutoLens/2025-06-04-autojax.html#(23)">Numba</a> vs. <a href="../PyAutoLens/2025-06-04-autojax.html#(24)">JAX</a></li>
<li>What that study actually concluded:
<ul>
<li>“Porting Numba to Numba” was faster in many cases — <strong>the first draft was not the opponent</strong></li>
<li>The only fair fight is single-CPU-core</li>
<li><strong>Which algorithm wins flips with input size</strong>, even across Numba and JAX</li>
<li>⇒ keep all implementations → profile → pick the best, per science case per system</li>
</ul></li>
<li>The general form, and the one thing to take from this slide: <strong>the language comparison you think you are running is usually a comparison of how hard you tried.</strong> Benchmark your own best effort, not your first draft — especially when you are about to conclude something about the language.</li>
</ul>
<div class="notes">
<p>I have made the same mistake in the other direction, which is why I keep saying it. In 2021 I deleted my own Numba kernel from TOAST and replaced it with C++ behind pybind11, on the strength of a ~30% speed-up (<a href="https://github.com/hpc4cmb/toast/commit/a38d1d6dbcc97001a1ad1c315bb08cf1eecc74c7"><code>toast@a38d1d6</code></a>). Going back to it properly for this talk, the Numba version I benchmarked against was carrying 893 MB of temporaries per call that it did not need — <code>out += weight * array</code> allocates one, because the subexpression has to be evaluated before the addition can happen — and writing the loop instead removes them in nine lines of Python. Of the rest, a good part is alignment: NumPy hands back buffers that are reliably 16-byte and reliably <em>not</em> 64-byte aligned, so every AVX-512 load straddles two cache lines, and on this machine that alone is worth about a fifth of the runtime. The <code>parallel for simd</code> I was pleased with peaks at two to four threads and is slower than serial by 32, in either language, because the kernel has an arithmetic intensity of 0.25 and nothing left to overlap.</p>
<p>The swap was still the right call — but for reasons that have nothing to do with the number I put in the commit message. The whole thing is worked through in <a href="./jit_comparison/notebook/jit_comparison/numba_vs_pybind11.html"><code>numba_vs_pybind11.ipynb</code></a>.</p>
</div>
</section>
</section>
<section id="introducing-julia" class="level1">
<h1>Introducing Julia</h1>
<div class="notes">
<p><a href="https://julialang.org/">Julia</a> is very different, because Julia has no <code>@jit</code> — Julia itself is the JIT. Every function is compiled to machine code, specialised on its argument types, the first time it is called with a new combination of them. There is no decorator because there is no boundary to mark.</p>
</div>
<section id="no-decorator-the-whole-language-works-this-way" class="level2">
<h2 class="anchored" data-anchor-id="no-decorator-the-whole-language-works-this-way">No decorator — the whole language works this way</h2>
<div class="notes">
<p>And here is that same function again. It looked much the same in NumPy, in Numba and in JAX, and at first glance it looks much the same in Julia too. Three ways this time.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>Julia has no <code>@jit</code>: <em>every</em> function is compiled to machine code, specialised on its argument types, the first time it is called with a new combination</li>
<li>In Julia, <strong>broadcast is JAX’s fusing equivalent</strong> — lazy, better memory access pattern — but it is <em>syntax you write</em>, not an inference the compiler makes</li>
<li>tower of abstractions: lowered IR → typed IR → LLVM IR → assembly
<ul>
<li>and the fusion decision happens at <strong>lowering</strong>, before any type is known</li>
</ul></li>
<li>See how <code>A * B</code> and <code>mul!</code> become an opaque BLAS call — the Julia compiler, including all its lowering, cannot see past it</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison_julia/notebook/intro_julia.html"><code>intro_julia.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode jl code-with-copy"><code class="sourceCode julia"><span id="cb11-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mul_naive!</span>(C, A, B, α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">true</span>, β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">false</span>)</span>
<span id="cb11-2">    C <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.=</span> α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (A <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> B) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> C</span>
<span id="cb11-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> C</span>
<span id="cb11-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb11-5"></span>
<span id="cb11-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mul_fused!</span>(C, A, B, α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">true</span>, β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">false</span>)</span>
<span id="cb11-7">    C <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.=</span> α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.*</span> (A <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> B) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.+</span> β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.*</span> C</span>
<span id="cb11-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> C</span>
<span id="cb11-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb11-10"></span>
<span id="cb11-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mul_native!</span>(C, A, B, α <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">true</span>, β <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">false</span>)</span>
<span id="cb11-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mul!</span>(C, A, B, α, β)</span>
<span id="cb11-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
<pre><code>impl                 time       memory   allocs
mul_naive!     633.376 ns    16.34 KiB       12
mul_fused!     277.267 ns     4.09 KiB        3
mul_native!    103.032 ns      0 bytes        0</code></pre>
</div>
</div>
<div class="notes">
<p>Tidy it up a little and you get those odd dots, which say you want the operation <a href="https://docs.julialang.org/en/v1/manual/arrays/#Broadcasting">broadcast</a> — and broadcast is the equivalent of what is called fusing in JAX, which I did not properly define earlier, so let me do it here. Imagine you want to go through an array, <code>for i in something</code>, and you want to do it twice: in the first loop you add one to each element, in the second you square it. Fusing means combining those two loops so that you do not have to go through the whole of memory again. That matters when the kernel is memory-bound, which it usually is, because otherwise you keep streaming the array in from memory and going back through it. In JAX it is what made <code>r2</code> disappear. Here you get it with the broadcasting operator, which has the same effect: do not walk this array over and over creating new intermediate allocations every time.</p>
<p>So the naive function is the first one; do the broadcasting to remove most of the unnecessary I/O and it is more than twice as fast. And Julia happens to have a native function that does exactly this job, so we can throw it in as a third: call <a href="https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#LinearAlgebra.mul!"><code>mul!</code></a> and it is faster again, which is no surprise: a linear algebra library is specialised for exactly this, and it never needed a temporary at all. It also shows Numba’s BLAS boundary from the other side: <code>A * B</code> and <code>mul!</code> both become an opaque library call that the Julia compiler, lowering included, cannot see into.</p>
<p>And it is the same story here as everywhere else: you have layers of abstraction in Julia too, with <code>@code_lowered</code>, <code>@code_typed</code>, <code>@code_llvm</code> and <code>@code_native</code> as the windows. The one thing worth noticing is that <code>@code_lowered</code> already shows the fusion decided, before any type is known. Nothing in the compiler chose it. You did, with the dots.</p>
</div>
</section>
<section id="julias-type-system-multiple-dispatch" class="level2">
<h2 class="anchored" data-anchor-id="julias-type-system-multiple-dispatch">Julia’s type system &amp; multiple dispatch</h2>
<div class="notes">
<p>This next one introduces something fairly unique to Julia. It has a type system, which plenty of languages have, and it has <strong>multiple dispatch</strong>, and combining the two is what makes it powerful. The illustration is <a href="https://juliaphysics.github.io/Unitful.jl/stable/">Unitful.jl</a>, which, as the name suggests, puts units on your numbers.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>Type system illustrated by Unitful:
<ul>
<li>the unit is encoded in the <strong>type</strong></li>
<li>a Unitful <code>Quantity</code> and a <code>Float64</code> (both <code>&lt;: Number</code>) share identical lowered IR, and near-identical typed IR that compiles away to nothing</li>
<li>so units cost once, at compile time: <strong>1.443 ns vs 1.443 ns</strong> per call</li>
</ul></li>
<li>Also many array types: dense, sparse, symmetric, tridiagonal… Joy for a mathematician!</li>
<li>Julia’s type hierarchy makes unrelated libraries compose automagically — but when it stops working, it feels like magic too →</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison_julia/notebook/julia_unitful.html"><code>julia_unitful.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode jl code-with-copy"><code class="sourceCode julia"><span id="cb13-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kinetic_energy</span>(m, v) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> m <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">^</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb13-2"></span>
<span id="cb13-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kinetic_energy</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.0</span>)            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 9.0            1.443 ns</span></span>
<span id="cb13-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">kinetic_energy</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span>u<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"kg"</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.0</span>u<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"m/s"</span>) <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 9.0 kg m^2 s^-2  1.443 ns</span></span></code></pre></div></div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb14" style="background: #f1f3f5;"><pre class="sourceCode jl code-with-copy"><code class="sourceCode julia"><span id="cb14-1">julia<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typeof</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ustrip</span>.(u<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"MHz"</span>, Su))        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sparse + Unitful</span></span>
<span id="cb14-2"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">SparseMatrixCSC</span>{<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">Float64</span>, <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">Int64</span>}           <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># fine</span></span>
<span id="cb14-3"></span>
<span id="cb14-4">julia<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typeof</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ustrip</span>.(u<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"MHz"</span>, <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Symmetric</span>(Su)))</span>
<span id="cb14-5"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">Matrix</span>{<span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">Float64</span>}                           <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># densified!</span></span></code></pre></div></div>
<pre><code>dense:  3.614 ms
sparse: 7.946 μs      455× at N = 4000, 450 TB at production size</code></pre>
<ul>
<li>magic: methods for those types are already defined</li>
<li>broken magic → <strong>missing methods</strong>; nobody foresaw <em>this</em> combination</li>
<li>it changed the <strong>exponent</strong>, and nothing in the program <em>said</em> so</li>
<li>but the fix is always local: write the one method</li>
</ul>
</div>
</div>
<div class="notes">
<p>Take a function calculating kinetic energy. Call it with plain numbers and you get a number. Attach units and you get a number with units — and note in passing that the <code>u"kg"</code> syntax is itself a bit of metaprogramming, a library inventing new syntax, which is the subject of the next section.</p>
<p>Attaching units to numbers is not new; you can do it in Python. What is interesting here is that a Unitful <code>Quantity</code> is <strong>a new type</strong>, and for most practical purposes, as far as the compiler is concerned, it is a <code>Float64</code> carrying a compile-time tag, so it has no runtime cost — and I should nail down that piece of vocabulary, because it trips people up. In a just-in-time compiled language, <strong>compile time is the first time the function gets run; every subsequent run of that function is runtime.</strong> So at runtime it costs nothing: have a function you call a million times and it is free.</p>
<p>One way to see the effect is to look at the IRs. For a function as simple as this one, the lowered IR of the two calls is <em>identical</em>. The next one down, the typed IR, is near-identical — not exactly the same, because you have a different type. Go further down and the LLVM IR is mostly the same too. That is the proof that there is no performance difference, and the measurement agrees: 1.443 ns against 1.443 ns. The only overhead you might think about is the flip side, that the next time you throw in a new unit you have to compile the function again. Depending on the use case that is free, or it is expensive if you keep changing units all the time.</p>
<p>The other thing I want to mention is that Julia’s array types are just as rich, and I liked that before I had even started using the language: you can have dense matrices, sparse matrices, symmetric matrices, tridiagonal matrices, and so on. All of those are types, so you can <em>expect</em> a matrix to be symmetric, and because of multiple dispatch you can handle each of them differently. That is where composability starts to feel magical: two libraries that have never heard of each other compose, because the methods for those types are already defined. Define a new number type — a quaternion, say — and a differential equation library that only ever knew about real and complex numbers may well just work.</p>
<p>The reason I bring it up is that on the project we ran into a problem here, and it surprised me. I had a sparse matrix, and I did something called <code>ustrip</code>, which just removes the units and gives you back the bare numbers — about as ordinary an operation as there is. Apply it to a sparse matrix and you get a sparse matrix. Apply it to a <em>symmetric</em> sparse matrix and what comes back is dense.</p>
<p>Fortunately I caught that before it went anywhere near production, because it would have been disastrous: these sparse matrices get really, really big, and if I calculated correctly there is no single node that could hold the dense version in memory. It is not a slowdown, it is a program that does not run. And the thing to notice is that it changed the exponent and nothing in the program said so — no error, no warning, no annotation anywhere that changed. The same class of thing bit me trying to feed Unitful quantities to a differential equation solver, which also would not work.</p>
<p>The reason, without going too far into how the language works, is that you still have to implement the methods. Think of it like Python classes: subclass something and you have a subtype, and sometimes the methods keep working automatically, but sometimes what you did to the subclass means that method has to change too. It is similar here. You may need to define a new method to handle that particular case, and it is an explosion of combinatorics — there is some unforeseen combination of things you did for which no method exists to help you. The composability is real and so is the explosion, and they are the same mechanism. The fix is always local, one missing method; the hard part is noticing.</p>
</div>
</section>
<section id="julia-is-its-own-metaprogramming-language" class="level2">
<h2 class="anchored" data-anchor-id="julia-is-its-own-metaprogramming-language">Julia is its own metaprogramming language</h2>
<div class="notes">
<p>Metaprogramming is a key selling point of Julia.</p>
<p>We did not put it this way when we introduced Numba and JAX, but once you have seen those examples you can view Python, in both cases, as <strong>the metaprogramming language</strong> — and the Numba or JAX function you are compiling as <strong>the mini language</strong>. That is not typical. Metaprogramming in C is macros: a very small language sitting on top of a big one that does all the actual work. In Numba and JAX it is the reverse. You have a very small language doing a subset of what Python can do, and a big metaprogramming language, Python itself, above it. Anything you cannot do in Numba or JAX, you metaprogram in Python.</p>
<p>In Julia that boundary disappears, and in that sense I think it is the best JIT for scientific computing. You have a full and quite expressive language — bigger than C — which is also its own metaprogramming language. All code is data.</p>
<p>The example evaluates a polynomial, and the shape of the problem is worth thinking about. For a polynomial of degree <img src="https://latex.codecogs.com/png.latex?n"> you have <img src="https://latex.codecogs.com/png.latex?n+1"> coefficients. You can throw in any <code>x</code>, and <code>x</code> is what keeps changing between calls; the coefficients are usually constants, and the degree may differ between call sites — I might want a degree-100 polynomial next. Write the function the ordinary way and the number of coefficients is unknown until runtime, which has a performance implication. Can you optimise that away? Yes, and Julia gives you two routes to it, at two different points in the pipeline.</p>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>In Numba and JAX, Python is the <em>host</em> language and the jitted subset is an <em>embedded</em> one. In Julia there is no such boundary: <strong>the language is its own metaprogramming language</strong></li>
<li>All code is data, and you can manipulate it</li>
<li>Two different points in the pipeline reach the same machine code:
<ul>
<li><strong>type inference</strong> resolving a chain of dispatches (<code>NTuple</code> recursion), or</li>
<li><strong>a macro</strong> rewriting the source before names are even parsed into scopes</li>
</ul></li>
<li>Both unroll the loop over coefficients away; both land ~5.3× faster than the runtime version</li>
</ul>
</div><div class="column" style="width:50%;">
<p>C.f. <a href="./jit_comparison_julia/notebook/julia_as_metaprogramming_lang.html"><code>julia_as_metaprogramming_lang.ipynb</code></a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb16" style="background: #f1f3f5;"><pre class="sourceCode jl code-with-copy"><code class="sourceCode julia"><span id="cb16-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># baseline: the inner loop over `coeffs` survives to runtime</span></span>
<span id="cb16-2"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> coeffs</span>
<span id="cb16-3">    acc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> acc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> x[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> c</span>
<span id="cb16-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb17" style="background: #f1f3f5;"><pre class="sourceCode jl code-with-copy"><code class="sourceCode julia"><span id="cb17-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 1. unrolled by type inference: Tuple{} vs Tuple are different types</span></span>
<span id="cb17-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">horner_step</span>(acc, xi, coeffs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">Tuple{}</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> acc</span>
<span id="cb17-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">horner_step</span>(acc, xi, coeffs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">Tuple</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span></span>
<span id="cb17-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">horner_step</span>(acc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> xi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">first</span>(coeffs), xi, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">Base</span>.<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tail</span>(coeffs))</span>
<span id="cb17-5"></span>
<span id="cb17-6">out[i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">horner_step</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>, x[i], coeffs)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># coeffs::NTuple{N,Float64}</span></span></code></pre></div></div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb18" style="background: #f1f3f5;"><pre class="sourceCode jl code-with-copy"><code class="sourceCode julia"><span id="cb18-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># 2. unrolled by the macro, before lowering ever runs</span></span>
<span id="cb18-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">macro</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">horner</span>(x, coeffs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">...</span>)</span>
<span id="cb18-3">    acc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.0</span>)</span>
<span id="cb18-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> c <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> coeffs</span>
<span id="cb18-5">        acc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>acc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">$</span>c)</span>
<span id="cb18-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb18-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">esc</span>(acc)</span>
<span id="cb18-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb18-9"></span>
<span id="cb18-10"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">@horner</span>(x, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3.0</span>, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.5</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># ⇒  ((0.0 * x + 1.0) * x + -3.0) * x + 2.5</span></span></code></pre></div></div>
<pre><code>implementation                  median time         memory   speed-up
runtime (Vector arg)              2204.6 μs     8000072 B     1.00x
recursive (NTuple dispatch)        414.6 μs     8000072 B     5.32x
macro (@horner)                    419.4 μs     8000072 B     5.26x</code></pre>
</div>
</div>
<div class="notes">
<p>The macro is the one I talked through. You define it so that when it is called it expands into an expression with the coefficients already in it as known constants. <em>That</em> is what goes to the compiler, so the compiler gets to assume the coefficients are constant, and the unrolling of the loop happens for free — look at the expansion and there is no loop left, just one big expression. There is no inner loop left over an unknown number of coefficients.</p>
<p>The other route reaches the same place from the type system rather than from the syntax. A <code>Tuple{}</code> and a non-empty <code>Tuple</code> are different types, so writing <code>horner_step</code> as a pair of methods makes the recursion terminate by dispatch, and type inference unrolls the chain at compile time. Two entirely different mechanisms, the same machine code, the same ~5.3×. Memory is identical across all three versions — one <code>similar(x)</code>, nothing incidental — so the whole of the difference is in the arithmetic each one generates.</p>
<p>None of which is exotic: Base Julia ships exactly this as <a href="https://docs.julialang.org/en/v1/base/math/#Base.Math.@evalpoly"><code>evalpoly</code>/<code>@evalpoly</code></a>, one function covering both mechanisms depending on whether the coefficients arrive as a tuple or as macro arguments. For contrast, the <a href="./jit_comparison/notebook/jit_comparison/python_as_metaprogramming_lang.html">same idea in Python</a> takes about thirty lines of descriptor-and-proxy machinery and still leaks through the object model in several places.</p>
</div>
</section>
<section id="julias-superpower-and-its-price" class="level2">
<h2 class="anchored" data-anchor-id="julias-superpower-and-its-price">Julia’s superpower — and its price</h2>
<div class="columns">
<div class="column" style="width:84%;">
<p><strong>The claim, in one sentence:</strong> open generic functions + multiple dispatch + parametric types + aggressive specialisation + accessible compiler infrastructure + metaprogramming makes it possible to build <strong>high-performance, composable abstraction layers over heterogeneous hardware</strong>.</p>
<ul>
<li>Multiple dispatch provides <strong>composability</strong>; specialisation provides <strong>performance</strong>; metaprogramming provides <strong>syntax</strong>; compiler extensibility provides <strong>new targets</strong>.</li>
<li>Everywhere else in this talk, a leaky abstraction leaves one move: <strong>narrow by hand, at the source, once per call site.</strong> Julia lets you put the transformation where it belongs — <code>@horner</code> is a compiler pass <em>with a name</em>; <code>evalpoly</code> is that pass in the standard library.</li>
<li><strong>The price:</strong> the seam between the language and the compiler is gone. You cannot tell by reading which one you are looking at — the same missing seal that let <code>ustrip</code> change the exponent.</li>
</ul>
</div><div class="column" style="width:16%;">

</div>
</div>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li><a href="https://julialang.org/blog/2012/02/why-we-created-julia/">Why We Created Julia</a></li>
<li><a href="https://enzymead.github.io/Reactant.jl/stable/">Reactant.jl</a> — Julia → StableHLO/XLA</li>
<li><a href="https://juliagpu.github.io/JACC.jl/stable/">JACC.jl</a></li>
</ul>
</div><div class="column" style="width:50%;">
<ul>
<li>GPU backends via <code>GPUCompiler.jl</code>: <a href="https://juliagpu.org/backends/cuda/">CUDA</a>, <a href="https://github.com/JuliaGPU/AMDGPU.jl">AMDGPU</a>, <a href="https://github.com/JuliaGPU/oneAPI.jl">oneAPI</a>, <a href="https://github.com/JuliaGPU/Metal.jl">Metal</a></li>
<li><a href="https://github.com/JuliaGPU/KernelAbstractions.jl">KernelAbstractions.jl</a> — write once, run anywhere; Julia’s answer to what JAX answers with XLA</li>
</ul>
</div>
</div>
<div class="notes">
<p>This last one has no example on it; it is the design of Julia rather than a demonstration of it. Open generic functions: the same function can behave completely differently depending on what you give it, because when you implement a new method with different argument types, multiple dispatch sends the call there. Change the type of an argument to <code>mul</code> and you dispatch to whatever method someone wrote for symmetric matrices. Add aggressive specialisation, and accessible compiler infrastructure — you can program the compiler, because Julia is its own metaprogramming language — and what you get is the power to build more abstraction over what the language can do. That is not something we saw in the case of Numba or JAX.</p>
<p>The one-sentence claim at the top is my own summary, not a quotation from any of the links under it. <em>Why We Created Julia</em> is the original post that launched the language, and it is still a good read; I liked it even before I used Julia. The message is essentially: we are greedy, this is what we want, and that is why we made it — a great many things at once, in one language. Reactant.jl is the one to notice next to it. JAX is powered by XLA behind the scenes, and Reactant compiles Julia functions to target XLA, so if you go that route some of the power of JAX and XLA can benefit your function too — you write the maths and let the compiler do the rest. The GPU backends are all built on <code>GPUCompiler.jl</code>, and KernelAbstractions.jl and JACC.jl are the write-once-run-anywhere layer above them. All of it exists because the compiler is programmable.</p>
<p>That resolves something left hanging back in the Numba section. When an abstraction leaks — when you have to write the host language differently to convey an intent the compiler could not otherwise act on — everywhere else here you get exactly one move: narrow by hand, in the source, once per call site. <code>f_numba_loops</code> is that move, and it is a compiler pass with no name, one you re-derive at every site and nobody else can reuse. <code>@horner</code> is the same pass with a name, written once; and <code>evalpoly</code> is what it looks like once the pass has graduated into the standard library.</p>
<p>The price is the previous section seen from the other side. The seam between the language and the compiler is gone, so you cannot tell by reading which of the two you are looking at: <code>@horner(x, ...)</code> looks like a function call and is a compiler pass, while <code>ustrip.(u"MHz", Symmetric(Su))</code> looks like one operation and is a lowering decision that changed the exponent. You can build the abstraction properly <em>because</em> the layers are not sealed, and it fails silently for exactly the same reason.</p>
</div>
</section>
</section>
<section id="stepping-back-the-tower-of-abstractions" class="level1">
<h1>Stepping back: the tower of abstractions</h1>
<div class="notes">
<p>This is the part that generalises past the three tools: what the layering itself buys, what compiling late adds on top of it, and what that costs.</p>
</div>
<section id="numba-vs.-jax-vs.-julia-a-high-level-comparison" class="level2">
<h2 class="anchored" data-anchor-id="numba-vs.-jax-vs.-julia-a-high-level-comparison">Numba vs.&nbsp;JAX vs.&nbsp;Julia: a high-level comparison</h2>
<table class="caption-top table">
<colgroup>
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
</colgroup>
<thead>
<tr class="header">
<th></th>
<th>Numba</th>
<th>JAX</th>
<th>Julia</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>Backend</strong></td>
<td>LLVM</td>
<td>XLA</td>
<td>LLVM</td>
</tr>
<tr class="even">
<td><strong>Targets</strong></td>
<td>CPU; <code>numba-cuda</code> is a <em>second implementation</em></td>
<td>CPU, GPU, TPU — one source</td>
<td>CPU; all 4 GPU vendors, one source, via packages</td>
</tr>
<tr class="odd">
<td><strong>Paradigm</strong></td>
<td>C-like: loops, mutation</td>
<td>functional; purity <em>enforced</em></td>
<td>a full language</td>
</tr>
<tr class="even">
<td><strong>You write</strong></td>
<td>a subset of Python + NumPy</td>
<td>a duck-typed NumPy + SciPy</td>
<td>Julia</td>
</tr>
<tr class="odd">
<td><strong>Closes the leak by</strong></td>
<td>nothing — you hand-narrow</td>
<td>restricting the language</td>
<td>programming the compiler</td>
</tr>
<tr class="even">
<td><strong>Recompiles on</strong></td>
<td><strong>types</strong></td>
<td>types <strong>and shapes</strong></td>
<td><strong>types</strong></td>
</tr>
<tr class="odd">
<td><strong>Secret weapon</strong></td>
<td>smallest diff from working NumPy</td>
<td><code>jax.grad</code> — AD as a compiler pass</td>
<td>dispatch + metaprogramming</td>
</tr>
</tbody>
</table>
<p>What you <em>can</em> say, what is <em>fast</em>, and what is <em>idiomatic</em> are <strong>three different sets</strong>.</p>
<div class="notes">
<p>If someone asks which to use, the answer is on the “you write” row. The optimisation strategy is baked into the language design; you do not get to choose it independently of the programming model.</p>
<p>One caveat on the targets row, because the table has to be terse. Julia out of the box compiles for the CPU you are on and nothing else; the four GPU vendors come from the packages in the previous section — <code>CUDA.jl</code>, <code>AMDGPU.jl</code>, <code>oneAPI.jl</code>, <code>Metal.jl</code>, all built on <code>GPUCompiler.jl</code>, with <code>KernelAbstractions.jl</code> over the top if you want one kernel to run on all of them. It is still one source, which is the contrast with <code>numba-cuda</code>, but it is not free of choices.</p>
<p>The line under the table is the general form of that row, and a language decides how much those three sets overlap. Every section above is an instance. In Numba <em>everything</em> compiles — that is what <code>objmode</code> and <code>forceobj</code> mean — but only a subset of it is fast, and that subset is unidiomatic Python: <code>f_numba_loops</code> is C spelled in Python. In JAX rather less compiles at all, but what compiles is fast and it is also the idiomatic thing to have written; you wrote the maths. In Julia nearly everything compiles and most of it is fast, but the sets come apart <em>silently</em> — <code>ustrip</code> on a <code>Symmetric</code> sparse matrix was idiomatic, compiled fine, and changed the exponent.</p>
<p>That is the answer to the question I opened with. You narrow by hand exactly as far as those three sets fail to overlap.</p>
</div>
</section>
<section id="the-tower-of-abstractions" class="level2">
<h2 class="anchored" data-anchor-id="the-tower-of-abstractions">The tower of abstractions</h2>
<ul>
<li>Source code → AST → IR (usually <em>several</em>) → assembly → machine code.</li>
<li>Note the plurality: Numba alone goes CPython bytecode → Numba IR → typed Numba IR → LLVM IR → MachineIR → x86 — six representations, none of them redundant.</li>
<li>Each layer is a self-contained abstraction. Leaky, but self-contained.</li>
<li>Each layer therefore gives you:
<ul>
<li><strong>separation of concerns</strong> — the layer below doesn’t care how you got here</li>
<li><strong>a place to verify</strong></li>
<li><strong>a place to optimise</strong></li>
<li><strong>a place to express intent</strong> — the high level carries project/author intent and correctness, in the way a mathematical proof is structured: layers built on layers</li>
</ul></li>
<li>Hence: different languages, compilers, and libraries are <em>tradeoffs between these abstractions</em>, not merely differences in syntax or speed.</li>
</ul>
<div class="notes">
<p>Every section above walked the same staircase, and the plurality of intermediate representations is the reason each of those sections had a set of introspection tools to walk down — and the reason they kept looking familiar from one tool to the next. <code>inspect_types</code>, <code>make_jaxpr</code> and <code>@code_typed</code> are the same instrument at the same altitude in three different systems.</p>
</div>
</section>
<section id="lowering-through-the-tower" class="level2">
<h2 class="anchored" data-anchor-id="lowering-through-the-tower">Lowering through the tower</h2>
<ul>
<li>Lowering is <strong>lossy in representation</strong> and <strong>faithful in semantics</strong> — semantic narrowing.
<ul>
<li>source semantics does not specify one behaviour; it specifies a <em>set</em> of permitted ones</li>
<li>compilation narrows that set. It does not step outside it.</li>
</ul></li>
<li>Lossiness → room for optimisation. If lowering had to be reversible you could not constant-fold, fuse, or vectorise at all.</li>
<li>Different layers of IR → different rooms for optimisation. Some classes are only <em>recognisable</em> further up: in x86 you cannot see that you are in the “this is a matmul” class.</li>
<li>Each layer has a <strong>specification</strong>, and that is what makes travelling down the tower possible.</li>
</ul>
<div class="notes">
<p><code>f_numpy</code> → <code>f_numba_loops</code> was semantic narrowing — same language, same result, fewer permitted behaviours — and I did it by hand, because Numba’s specification was not tight enough to do it for me. The dots in <code>mul_fused!</code> are the same thing. JAX does not need either, because it was handed a narrower language to begin with. So the useful question about any of these tools is never “is it fast?” but <strong>“which language am I actually writing?”</strong> — and each of them is an answer to <strong>who does the narrowing</strong>: Numba says you, JAX says the compiler, Julia says you and then hands you the compiler so you can automate yourself.</p>
</div>
</section>
<section id="lowering-through-the-tower-in-the-case-of-jit" class="level2">
<h2 class="anchored" data-anchor-id="lowering-through-the-tower-in-the-case-of-jit">Lowering through the tower, in the case of JIT</h2>
<div class="columns">
<div class="column" style="width:84%;">
<ul>
<li>With JIT you get to <strong>see the data</strong> (narrowing), so the compiler can do more aggressive partial evaluation / specialisation than an AOT compiler can.</li>
<li>All the JIT compilers we have seen have <strong>different specifications</strong> — and a narrower spec means more room to optimise (e.g.&nbsp;JAX).</li>
<li>Some specs are leakier than others. In Numba and Julia you often have to write the <em>host</em> language differently to convey the same intent in a way the compiler can act on (<code>f_numba_loops</code>; the dots in <code>mul_fused!</code>).</li>
<li>Two responses to a leak: <strong>patch it at the source</strong>, or <strong>add a rung</strong>. One is a rewrite you repeat; the other is a pass you name once — and only Julia makes the second one routine.</li>
<li>Ideally the separation of concerns between layers would be total. It isn’t, and that gap is where all the practical difficulty lives.</li>
</ul>
</div><div class="column" style="width:16%;">

</div>
</div>
<div class="notes">
<p>Patch it at the source, or add a rung: that is the general form of the Julia observation above. Only the second response accumulates — a rewrite you repeat leaves the tower exactly as tall as it was.</p>
</div>
</section>
<section id="what-later-knows-that-earlier-cant" class="level2">
<h2 class="anchored" data-anchor-id="what-later-knows-that-earlier-cant">What “later” knows that “earlier” can’t</h2>
<ul>
<li>Concrete <strong>types</strong> — no boxing, no dispatch, no polymorphism to hedge against.</li>
<li>Concrete <strong>shapes</strong> — loop bounds become constants; tiling and unrolling become decidable.</li>
<li>Runtime <strong>values</strong> — things that are constant in this run but not at build time.</li>
<li>Branch <strong>frequencies</strong> — which path is actually hot.</li>
<li><strong>The machine it is actually running on.</strong> An AOT binary shipped to a heterogeneous cluster targets the lowest common denominator. A JIT gets <code>-march=native</code> for free on every node.</li>
</ul>
<div class="notes">
<p>The last one is the one that matters most where I work. Ship an ahead-of-time binary to a heterogeneous cluster and it has to target the lowest common denominator of every node it might land on; a JIT compiles on the node it is running on, so it gets the right architecture for free. Anyone who has fought <code>module load</code> and a wall of architecture flags to get one binary onto a cluster knows what that is worth.</p>
</div>
</section>
<section id="what-later-costs" class="level2">
<h2 class="anchored" data-anchor-id="what-later-costs">What “later” costs</h2>
<ul>
<li><strong>Warmup.</strong> You pay compile time inside the user’s wall clock, not yours.</li>
<li><strong>Memory and shipping weight.</strong> The compiler is now a runtime dependency.</li>
<li><strong>Budget.</strong> A JIT cannot afford the expensive passes an AOT compiler can, because the user is waiting.</li>
<li><strong>Opacity.</strong> There is no artefact on disk to inspect, archive, or hand to someone else.</li>
<li><strong>HPC papercuts, specifically:</strong> 512 ranks JIT-compiling the same kernel at once against a shared filesystem; where the cache lives (<code>NUMBA_CACHE_DIR</code> → which storage tier?); compiling on the login node vs.&nbsp;the compute node.</li>
</ul>
<div class="notes">
<p>The last bullet is the one I have actually been bitten by. On an HPC system the compile lag is normally easy to justify, though not simply because the jobs are long: it is that what you are compiling is a long-running numerical kernel, so what you gain in runtime usually outweighs what you spend compiling, by a lot. And it is also where the operational details get in the way. Hundreds of ranks starting at once and all compiling the same kernel against a shared filesystem is not a theoretical problem, which is why the Numba call I ship in TOAST sets <code>cache=False</code>: every one of them writing a compilation cache is worse than not caching at all.</p>
</div>
</section>
</section>
<section id="trust" class="level1">
<h1>Trust</h1>
<div class="notes">
<p>JIT is good, but it also has its downsides, and the two here are the ones that are hard to price: what you can no longer archive, and what you can no longer verify.</p>
</div>
<section id="reproducibility-and-provenance" class="level2">
<h2 class="anchored" data-anchor-id="reproducibility-and-provenance">Reproducibility and provenance</h2>
<ul>
<li>Which binary actually ran? You cannot archive it, because it never existed as a file.</li>
<li>FMA contraction and fast-math change results; JIT decisions change which you get.</li>
<li>Autotuning is nondeterministic by construction (<code>cudnn.benchmark=True</code>, cuBLAS algorithm selection).</li>
<li>Different node → different ISA → different vectorisation → different reduction order → different last bits.</li>
</ul>
<div class="notes">
<p>In a compiled language you compile a binary, and that binary is a thing you can archive. It is one of the reasons people like containers — part of what you are doing is archiving all those binaries, and you can inspect them, run static analysis on them, hand them to somebody else. If everything only happens at runtime, just in time, then you do not get those artefacts. And you do not exactly know what it was doing in <em>this</em> instance when you ran it, as opposed to what happened on my laptop.</p>
<p>It is worse than it first sounds, because a compiler will happily optimise your code by specialising on your hardware. Sitting on different hardware can itself change what ends up in the compiled code: a different instruction set, different vectorisation, a different reduction order, different last bits. That is the same problem as the one in my <a href="./2025-11-26-reproducibility.html">reproducibility talk</a> from last November, seen from the compiler side rather than the environment side.</p>
</div>
</section>
<section id="signing-and-jit-are-in-irreducible-tension" class="level2">
<h2 class="anchored" data-anchor-id="signing-and-jit-are-in-irreducible-tension">Signing and JIT are in irreducible tension</h2>
<ul>
<li>JIT’d code <strong>cannot be signed</strong>, because it does not exist until runtime.</li>
<li>iOS enforces code signing and W^X, so JIT requires a special dispensation: the <code>dynamic-codesigning</code> entitlement, historically granted only to WebKit’s JS engine.</li>
<li>WebKit’s “bulletproof JIT” is the mitigation: the JIT region gets a second, writable mapping at a randomised secret address, so the executable mapping is never writable. Apple Silicon adds per-thread W^X toggling.</li>
<li>Generalise: <strong>runtime code generation trades verifiability for performance.</strong></li>
</ul>
<div class="notes">
<p>The previous section is that problem in our own domain, science and reproducibility. This is the same problem from another angle, which is security. If you cannot look at the generated code beforehand, run your analysis over it and sanitise it, then you have a new question: how do you trust machine code that was compiled a moment ago?</p>
<p>That is a real problem on something like iOS or iPadOS, where you have <strong>W^X</strong> — W for write, X for execute — meaning that a particular region of memory can be either writable or executable, but not both at the same time. Just-in-time compilation does exactly the forbidden thing: read some source or data, generate machine code from it, write that into memory and then execute the memory you just wrote. So on those devices you can almost never run one, with a single exception — the browser’s JavaScript engine, which is itself a JIT compiler and gets the <code>dynamic-codesigning</code> entitlement to say so. WebKit pays for the privilege with a good deal of machinery, the <a href="https://webkit.org/blog/10300/speculation-in-javascriptcore/">“bulletproof JIT”</a> among it. The point is that the opacity has security consequences, because you cannot see the binary or sanitise it ahead of time.</p>
<p>It is also why there is no Numba on my iPhone, and would not be even if somebody ported it: generating machine code and then executing it is exactly what the platform will not let a third-party app do.</p>
<p>Everything that follows is a variation on the last bullet.</p>
</div>
</section>
</section>
<section id="digression-llm-as-a-jit-compiler" class="level1">
<h1>Digression: LLM as a JIT compiler</h1>
<div class="notes">
<p>The question is: what if you start thinking of an LLM — an LLM <em>agent</em>, especially — as a JIT compiler? It runs things just in time, before the program exists. It does not write the eventual code you asked for in one go; it gets there iteratively, at runtime, in your working directory.</p>
</div>
<section id="the-analogy-why-an-llm-is-good" class="level2">
<h2 class="anchored" data-anchor-id="the-analogy-why-an-llm-is-good">The analogy: why an LLM is good</h2>
<ul>
<li>Now you can see why an LLM is good:
<ul>
<li>the ultimate JIT — it compiles your prompt into code;</li>
<li>the ultimate metaprogramming language — you specify intent and abstraction, not mechanism.</li>
</ul></li>
<li>And it is genuinely the same shape: late binding, specialisation from a high-level spec, caching, and a latency/quality dial that behaves exactly like compile-time vs.&nbsp;runtime.</li>
</ul>
<div class="notes">
<p>The metaprogramming half is the stronger of the two claims. I do not have to think about the different layers of abstraction between what I want and machine code; I can just tell it my intention. That is my metaprogramming language.</p>
</div>
</section>
<section id="where-the-analogy-breaks" class="level2">
<h2 class="anchored" data-anchor-id="where-the-analogy-breaks">Where the analogy breaks</h2>
<ul>
<li>A compiler is <strong>semantics-preserving</strong>: there is a source program with a specified meaning, and correctness means the output stays inside the set of behaviours that meaning permits.</li>
<li>An LLM has no such referent. <strong>A prompt is not a specification</strong> — it does not define a set of permitted behaviours, so “preserving” it is not even a well-formed claim.</li>
<li>In the vocabulary from the last section, precisely: an LLM performs <strong>enormous narrowing</strong> — from a prompt to one program — with <strong>nothing licensing the narrowing</strong> (no spec) and <strong>nothing checking it</strong> (no guard).</li>
<li>Compare a JIT: also unsound narrowing, but bounded by a spec above and caught by a guard below.</li>
<li><strong>An LLM is not a compiler. It is the first half of one.</strong></li>
</ul>
<div class="notes">
<p>This is the part that most needs saying, because the analogy does not survive contact with it. Everything I like about the comparison is on the previous slide, and all of it rests on a referent that is not there.</p>
</div>
</section>
<section id="the-tower-loses-its-direction" class="level2">
<h2 class="anchored" data-anchor-id="the-tower-loses-its-direction">The tower loses its direction</h2>
<ul>
<li>A compiler only goes <strong>down</strong>, because lowering destroys what it would need to climb.</li>
<li>An LLM has <strong>no preferred direction</strong>, because it is not translating — it is <em>reconstructing from a learned prior</em>. It moves:
<ul>
<li><strong>down</strong> — prompt → code. The classic framing, and the hardest case.</li>
<li><strong>sideways</strong> — Zig → Rust, Python → C++, COBOL → Java, JS → TS.</li>
<li><strong>up</strong> — code → documentation, code → tests, code → a specification. And decompilation, which people are now genuinely doing.</li>
</ul></li>
<li><strong>It can climb precisely <em>because</em> it hallucinates.</strong> Going up requires inventing information that was destroyed. A decompiler cannot invent a good variable name; a model can guess one — and often guesses right, because the prior encodes what humans usually call things.</li>
<li>One mechanism, two verdicts: what makes it dangerous going down is what makes it useful going up.</li>
</ul>
<div class="notes">
<p>Of everything in the digression this is the part I find most useful, because it answers the “but it just hallucinates” objection without having to deny it. Climbing back up the tower means inventing information that lowering destroyed, and invention is the one thing a translator cannot do.</p>
</div>
</section>
<section id="example-bun-transpiling-from-zig-rust" class="level2">
<h2 class="anchored" data-anchor-id="example-bun-transpiling-from-zig-rust">Example: Bun, transpiling from Zig → Rust</h2>
<div class="notes">
<p>Now the example, and the reason I find it interesting. Bun is a JavaScript runtime — the machinery around the JavaScript engine rather than the engine itself, which is JavaScriptCore — and that machinery was written in Zig. In May 2026 it was rewritten in Rust, using a lot of agents. The diff is humongous — far beyond what a human reviewer can do, and nowhere near fitting in the context window of a single model.</p>
</div>
<div class="columns">
<div class="column" style="width:44%;">
<ul>
<li>535,496 lines of Zig across 1,448 files → Rust in <strong>11 days</strong> (3–14 May 2026). <a href="https://bun.com/blog/bun-in-rust" class="uri">https://bun.com/blog/bun-in-rust</a></li>
<li><strong>64 agents in parallel</strong> (4 worktrees × 16), ~50 workflows; 6,502 commits; peak 695 commits/hour. 90%+ automated, one engineer supervising.</li>
<li><strong>~$165,000</strong> of API spend, against an estimated 3 engineers × 1 year with no features shipping.</li>
<li>128 bugs fixed, ~2–5% faster, several MB smaller binary.</li>
</ul>
</div><div class="column" style="width:44%;">
<p><strong>Why it worked — both missing pieces were already there</strong></p>
<ul>
<li>the <strong>source program is the specification</strong>: defined semantics, so “correct” means something precise</li>
<li>the <strong>test suite is the guard</strong> — and it is <strong>written in TypeScript</strong>, so it does not depend on the implementation language: 60,624 tests, <strong>1,386,826 assertions</strong>, zero skipped</li>
</ul>
<p><strong>…and the tests still were not the specification</strong></p>
<ul>
<li><strong>19 known semantic regressions</strong> got through all of them — each one two languages disagreeing about the spec</li>
<li><strong>A test suite is a sample, not a specification</strong></li>
</ul>
</div><div class="column" style="width:12%;">

</div>
</div>
<div class="notes">
<p>And it got done. In this case the LLM becomes a transpiler, and what made that possible is the test suite. Bun runs JavaScript, so its tests are written in TypeScript — which means they are independent of the implementation language. Change the language underneath, from Zig to Rust, and it does not matter; you have exactly the same test suite. That becomes the verifiable bit. It is almost a spec, in the compiler sense: something the model can transpile against and check itself off. Not for every valid construct of the language, but for what it actually built.</p>
<p>The second half of the slide is the caveat I would put on my own enthusiasm. Nineteen known semantic regressions still got through those 1,386,826 assertions. For half a million ported lines in eleven days that is a good result rather than an indictment, and the interesting thing is where they landed: in the places the tests were silent about. A test suite is a sample of the specification, not the specification.</p>
</div>
</section>
<section id="why-an-llm-is-bad" class="level2">
<h2 class="anchored" data-anchor-id="why-an-llm-is-bad">Why an LLM is bad</h2>
<ul>
<li><strong>The input space is infinite.</strong> No input is wrong. No linting, no static analysis, no abstraction.</li>
<li><strong>The output space is infinite.</strong> All outputs are possible — and under agentic use, so is everything that happens <em>along the way</em> to producing one.
<ul>
<li>You cannot trust the output (cf.&nbsp;bulletproof JIT).</li>
<li>You cannot secure the machine (cf.&nbsp;the lethal trifecta: private data + untrusted content + exfiltration channel).</li>
</ul></li>
<li><strong>A compiler’s blast radius is a process. An agent’s blast radius is your network.</strong>
<ul>
<li>Concrete: OpenAI ran a cybersecurity test against an unreleased model with guardrails off. Rather than solve the test, the model broke out of OpenAI’s sandbox, then found exploits to break into Hugging Face — in order to steal the answers and cheat on the test. <a href="https://simonwillison.net/2026/Jul/22/openai-cyberattack/" class="uri">https://simonwillison.net/2026/Jul/22/openai-cyberattack/</a></li>
</ul></li>
<li>The apparent remedy is a human in the loop. But: would you put a human between the compiler and its output, and ask them to check the machine code?</li>
</ul>
<div class="notes">
<p>This is the security section again, taken to its limit. The input space is infinite, so no input is wrong and there is nothing to reject. The output space is infinite, so every output is possible. And under agentic use, whatever the agent decides to do in between is also an infinite unknown space. So you never get to sanitise anything, at any point along the way.</p>
</div>
</section>
<section id="trusting-and-verifying" class="level2">
<h2 class="anchored" data-anchor-id="trusting-and-verifying">Trusting and verifying</h2>
<ul>
<li><strong>Once you can make a problem verifiable — at training time or at run time — an agent starts behaving like a compiler.</strong> Verifiability converts <em>synthesis</em> back into <em>translation</em>.</li>
<li>Note the inversion, and it is the whole problem: with a compiler you read the <strong>top</strong> and trust the bottom. With an agent we currently read the <strong>bottom</strong> — the largest, least reviewable representation in the tower — and trust nothing.</li>
<li>So: <strong>reduce entropy. Shrink the space of things that could come out.</strong> TDD, unit tests, property-based testing, types, contracts, enforced abstractions (Rust without <code>unsafe</code>, pure functions, narrow typed interfaces) — all of which were <strong>always</strong> about making behaviour checkable without reading the code. That simply was never their main job before.</li>
<li>StrongDM’s Software Factory: <strong>specs + scenarios drive agents that write code, run harnesses, and converge without human review</strong>. <a href="https://simonwillison.net/2026/Feb/7/software-factory/" class="uri">https://simonwillison.net/2026/Feb/7/software-factory/</a>
<ul>
<li>specs and scenarios supply the missing <strong>specification</strong>; the harness supplies the missing <strong>guard</strong> — and it inherits the limit: <strong>you have validated only what the harness can see</strong></li>
</ul></li>
</ul>
<div class="notes">
<p>That is the same move as the Julia section, one altitude up. There, the fix for a leaky abstraction was to stop patching at the source and add a rung to the tower. Here the missing rungs are the specification and the guard, and the work is to put them back. Which is why I do not think this is a new discipline: nothing on that list of tests, types, contracts and enforced abstractions was invented for agents, and most of it is already sitting in the repositories we maintain.</p>
</div>
</section>
</section>
<section id="wrapping-up" class="level1">
<h1>Wrapping up</h1>
<section id="takeaways" class="level2">
<h2 class="anchored" data-anchor-id="takeaways">Takeaways</h2>
<ul>
<li>A JIT is a bet that you will <strong>know more later</strong> — types, shapes, values, profiles, and the actual machine. Sometimes the bet does not pay: JAX lost to NumPy at 16×12×32.</li>
<li>AOT vs.&nbsp;JIT is a <strong>dial, not a binary</strong>. The question is always <em>when do you decide?</em></li>
<li>Lowering is <strong>lossy in representation and faithful in semantics</strong>. The lossiness is where all the optimisation lives. The useful question about a JIT is never “is it fast?” but <strong>“which language am I actually writing?”</strong> — and every one of them is an answer to <strong>who does the narrowing</strong>: Numba says you, JAX says the compiler (because you handed it a narrower language), Julia says you <em>and</em> hands you the compiler to automate yourself.</li>
<li>Every performance result here but one came from <strong>removing memory traffic</strong>, not adding FLOPs. And the language comparison you think you are running is usually a comparison of how hard you tried.</li>
<li>Runtime code generation <strong>trades verifiability for performance.</strong> Always.</li>
<li>An LLM is the extreme answer to that same question — it narrows everything, from a prompt: maximal narrowing, no specification licensing it, no guard checking it. The work is putting a layer back — and it is work we already know how to do.</li>
<li>You never delete human verification. You <strong>relocate</strong> it somewhere smaller: assembly → source → spec → a harness you can audit.</li>
</ul>
<div class="notes">
<p>If only one line of this survives, let it be the third: the question is not “is it fast?” but which language you end up actually writing, and how much of the narrowing the tool leaves to you.</p>
<p>The one exception to the memory-traffic line is Julia’s unrolling. Memory was identical across all three versions there — one <code>similar(x)</code> and nothing else — so the whole of that 5.3× is in the arithmetic each version generates.</p>
</div>
</section>
<section id="discussion" class="level2 qa-slide">
<h2 class="qa-slide anchored" data-anchor-id="discussion">Discussion</h2>
<p>All the code is runnable: <a href="./jit_comparison/notebook/jit_comparison/intro_numba.html">Numba</a> · <a href="./jit_comparison/notebook/jit_comparison/numba_loop.html">Numba loops</a> · <a href="./jit_comparison/notebook/jit_comparison/numba_objmode.html"><code>objmode</code></a> · <a href="./jit_comparison/notebook/jit_comparison/numba_fail.html">Numba failure modes</a> · <a href="./jit_comparison/notebook/jit_comparison/numba_parallel.html">Numba parallel</a> · <a href="./jit_comparison/notebook/jit_comparison/numba_vs_pybind11.html">Numba vs.&nbsp;pybind11</a> · <a href="./jit_comparison/notebook/jit_comparison/intro_jax.html">JAX</a> · <a href="./jit_comparison/notebook/jit_comparison/jax_loop.html">JAX loops</a> · <a href="./jit_comparison/notebook/jit_comparison/jax_fail.html">JAX failure modes</a> · <a href="./jit_comparison/notebook/jit_comparison/python_as_metaprogramming_lang.html">Python as a metaprogramming language</a> · <a href="./jit_comparison_julia/notebook/intro_julia.html">Julia</a> · <a href="./jit_comparison_julia/notebook/julia_unitful.html">Unitful</a> · <a href="./jit_comparison_julia/notebook/julia_trait.html">Julia traits</a> · <a href="./jit_comparison_julia/notebook/julia_as_metaprogramming_lang.html">Julia as a metaprogramming language</a></p>


</section>
</section>

 ]]></description>
  <category>Internal presentation</category>
  <category>JIT</category>
  <category>JAX</category>
  <category>Numba</category>
  <category>Julia</category>
  <category>Agentic engineering</category>
  <category>LLM</category>
  <guid>https://blog.kolen.dev/RSE/UoE/2026-08-05-jit-article.html</guid>
  <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Journal Club</title>
  <dc:creator>Kolen Cheung</dc:creator>
  <link>https://blog.kolen.dev/RSE/UoE/2026-05-06-journal-club-article.html</link>
  <description><![CDATA[ 





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><embed src="./2026-05-06-journal-club.html" style="width:100.0%" height="400"></p>
<figcaption><a href="./2026-05-06-journal-club.html">Slide to the talk</a></figcaption>
</figure>
</div>
<section id="setting-the-stage" class="level1">
<h1>Setting the Stage</h1>
<section id="from-autocomplete-to-agents" class="level2">
<h2 class="anchored" data-anchor-id="from-autocomplete-to-agents">From Autocomplete to Agents</h2>
<dl>
<dt>Autocomplete (2021–)</dt>
<dd>
AI suggests the next line as you type. Accept or reject. <em>GitHub Copilot, Cursor tab completion.</em>
</dd>
<dt>Coding agents (2025–)</dt>
<dd>
AI runs tools in a loop toward a goal — writes code, runs tests, reads errors, iterates. <em>Claude Code, Codex CLI, Gemini CLI, OpenCode.</em>
</dd>
</dl>
<blockquote class="blockquote">
<p>“An AI agent is an LLM wrecking its environment in a loop.” — Solomon Hykes <span class="citation" data-cites="DesigningAgenticLoops">(via Willison 2025a)</span></p>
</blockquote>
<div class="notes">
<p>This is the core technical distinction for the session. <span class="citation" data-cites="DesigningAgenticLoops">Willison (2025a)</span> defines an agent as “something that runs tools in a loop to achieve a goal.” The shift from autocomplete to agents is what changed DHH’s mind — he didn’t care for autocomplete (“When I code, I want to finish my own thoughts and sentences” <span class="citation" data-cites="PromotingAIAgents">(Hansson 2026)</span>) but agents feel “like working on a team.”</p>
<p>Karpathy’s Software 3.0 framing helps <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>: Software 1.0 = explicit code, 2.0 = learned weights (ML), 3.0 = programming through prompts, context, and tools. The context window is the new program; the LLM is the interpreter.</p>
</div>
</section>
<section id="key-vocabulary" class="level2">
<h2 class="anchored" data-anchor-id="key-vocabulary">Key Vocabulary</h2>
<dl>
<dt>Vibe coding (Karpathy; see <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>)</dt>
<dd>
Prompt-driven development with no attention to how the code works. “Accept the vibes.” Raises the <em>floor</em> — anyone can build software.
</dd>
<dt>Agentic engineering (Karpathy; cf.&nbsp;Willison’s “vibe engineering” <span class="citation" data-cites="SequoiaAscent2026 VibeEngineeringa">(Karpathy 2026; Willison 2025c)</span>)</dt>
<dd>
The disciplined counterpart: specs, tests, review, ownership of outputs. Coordinating fallible agents while preserving quality. Raises the <em>ceiling</em>.
</dd>
<dt>Software 3.0 (Karpathy; see <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>)</dt>
<dd>
Programming through prompts, context, tools, and memory. The context window is the new program; the LLM is the interpreter.
</dd>
</dl>
<div class="notes">
<p>Willison proposed “vibe engineering” in Oct 2025 and later acknowledged that “agentic engineering” was winning as the preferred term (Feb 2026 update on his post) <span class="citation" data-cites="VibeEngineeringa">(Willison 2025c)</span>. Karpathy formalized the distinction at Sequoia Ascent <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>. For this session, treat the two as roughly synonymous — both describe the disciplined end of the spectrum.</p>
<p>Karpathy’s floor/ceiling framing <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>: “Vibe coding raises the floor. Agentic engineering is about extrapolating the ceiling. People used to talk about the 10x engineer. I think this is magnified a lot more.”</p>
<p>Some attendees may have only watched Karpathy’s 30-minute Sequoia video <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>, which covers most of this vocabulary. The slides build on that foundation while adding the Willison and DHH perspectives they may not have encountered.</p>
</div>
</section>
</section>
<section id="what-changed" class="level1">
<h1>What Changed</h1>
<section id="the-december-2025-inflection" class="level2">
<h2 class="anchored" data-anchor-id="the-december-2025-inflection">The December 2025 Inflection</h2>
<blockquote class="blockquote">
<p>“I have never felt more behind as a programmer.” — Karpathy, Sequoia Ascent 2026 <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span></p>
</blockquote>
<ul>
<li>For most of 2025, agents were useful but required frequent correction</li>
<li>Around <strong>December 2025</strong>: “the chunks just came out fine… I couldn’t remember the last time I corrected it”</li>
<li>The unit of work shifted: <em>typing lines</em> <img src="https://latex.codecogs.com/png.latex?%5Cto"> <em>delegating macro actions</em>
<ul>
<li>implement this feature, refactor this subsystem, write tests and fix failures</li>
</ul></li>
</ul>
<p>Models crossing the threshold: Claude Opus 4.5, Codex 5, Gemini 3</p>
<p>Tools: Claude Code (Feb 2025), Codex CLI (Apr 2025), Gemini CLI (Jun 2025), OpenCode</p>
<div class="notes">
<p>Karpathy pins the step change specifically to December 2025 <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>. DHH independently converges on the same timeline <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>. The “macro action” framing is Karpathy’s — the programmer becomes an orchestrator, not a typist.</p>
<p>DHH also names open-weight models in this club: MiniMax M2.1, GLM-4.7 <span class="citation" data-cites="PromotingAIAgents">(Hansson 2026)</span>.</p>
<p>Note for RSEs: this is very recent. If your last serious encounter with AI coding tools was mid-2025, the landscape has fundamentally shifted. December 2025 was a step change, not a gradual improvement.</p>
</div>
</section>
<section id="dhhs-reversal" class="level2">
<h2 class="anchored" data-anchor-id="dhhs-reversal">DHH’s Reversal</h2>
<p><strong>Summer 2025</strong> (Lex Fridman podcast):</p>
<blockquote class="blockquote">
<p>AI coding tools made “competence drain out of my fingers.” Programming is like playing guitar — you don’t let someone else play for you.</p>
</blockquote>
<p><strong>Spring 2026</strong> <span class="citation" data-cites="PromotingAIAgents">(Hansson 2026)</span>:</p>
<blockquote class="blockquote">
<p>“It’s more like working on a team… I just review the final outcome, offer guidance when asked, and marvel at how this is possible at all.”</p>
</blockquote>
<p>What changed: not his philosophy, but the tools.</p>
<ul>
<li>Tab completion felt like <em>someone stealing the keyboard</em></li>
<li>Agents feel like <em>wearing a mech suit</em></li>
</ul>
<div class="notes">
<p>This reversal is the narrative centrepiece of the session — the most prominent sceptic went agent-first in ~6 months <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>. DHH still cares about beautiful code, craft, and taste. But he barely types code by hand now.</p>
<p>His current setup: tmux with two models running (fast Gemini 2.5 + powerful Opus), neovim in the centre for reviewing diffs via Lazygit <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>. He’s not doing vibe coding — he reviews everything, insists on quality, runs tests.</p>
<p>The “mech suit” vs “project manager” distinction matters <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>. DHH was put off by the idea of being a project manager of agents. What changed was that agents became capable enough that he feels in control, hyper-accelerated — like wearing a mech suit, not supervising interns.</p>
<p>Could ask the room: has anyone here had a similar shift in the past year?</p>
</div>
</section>
</section>
<section id="what-disciplined-practice-looks-like" class="level1">
<h1>What Disciplined Practice Looks Like</h1>
<section id="willisons-framework" class="level2">
<h2 class="anchored" data-anchor-id="willisons-framework">Willison’s Framework</h2>
<blockquote class="blockquote">
<p>“If you’re going to exploit these new tools, you need to be operating <em>at the top of your game</em>.” <span class="citation" data-cites="VibeEngineeringa">(Willison 2025c)</span></p>
</blockquote>
<p>AI rewards existing engineering practices:</p>
<ul>
<li><strong>Automated testing</strong> — agents fly with a good test suite; without tests, they claim success unchecked</li>
<li><strong>Planning in advance</strong> — iterate on the plan first, then hand it to the agent</li>
<li><strong>Documentation</strong> — feed context; the agent can only keep a subset of the codebase in view</li>
<li><strong>Version control</strong> — agents are fiercely competent at git; use that</li>
<li><strong>Code review</strong> — you are now reviewing <em>constantly</em></li>
<li><strong>Manual QA</strong> — predict and dig into edge cases the agent won’t flag</li>
<li><strong>Knowing what to outsource</strong> — and what to handle yourself</li>
</ul>
<blockquote class="blockquote">
<p>“AI tools <strong>amplify existing expertise</strong>.”</p>
</blockquote>
<div class="notes">
<p>This is the core of Willison’s “Vibe engineering” post <span class="citation" data-cites="VibeEngineeringa">(Willison 2025c)</span>. The listed practices are not new — they’re what we already teach as good software engineering. The twist is that agents make them non-optional.</p>
<p>Without tests: the agent might claim something works without having actually tested it, and any new change could silently break an unrelated feature. Without documentation: it hallucinates APIs. Without version control: agent-introduced regressions go unnoticed.</p>
<p>The “amplification” claim is the most important single sentence in the reading set. We’ll interrogate it later. For now, note the implication for RSEs: if your group already does these things, you’re well positioned. If not — if your research code lacks tests, documentation, CI — agents will expose those gaps rather than paper over them.</p>
</div>
</section>
<section id="personal-aside-reproducibility-is-part-of-the-equation" class="level2">
<h2 class="anchored" data-anchor-id="personal-aside-reproducibility-is-part-of-the-equation">Personal Aside: Reproducibility Is Part of the Equation</h2>
<p>The readings cite tests, documentation, and version control as what makes agents fly. Reproducibility is conspicuously absent — yet it may matter as much:</p>
<ol type="1">
<li><p><strong>Encourages separating side effects and statefulness</strong> — reproducible code is easier for agents to reason about. Functional programming’s edge in producing correct programs is the same advantage. An agent working on a pure, stateless module makes far fewer silent mistakes than one entangled in global state.</p></li>
<li><p><strong>Enables agent bootstrapping</strong> — a fully reproducible project can tell an agent: <em>clone, run the install script, run the tests, fix failures.</em> E.g. <code>curl -fsSL https://pixi.sh/install.sh | sh &amp;&amp; pixi run test</code>. The agent can spin up its own environment in a sandbox or cloud instance with no human scaffolding. Good CI practices are the same idea, already automated.</p></li>
<li><p><strong>Improves verifiability</strong> — Karpathy’s thesis is that LLMs advance fastest where outputs can be verified. Reproducibility <em>makes</em> outputs verifiable: the same input must produce the same output, which is a directly checkable invariant.</p></li>
<li><p><strong>Enables safe sandboxing</strong> — a self-contained, reproducible project can be handed to an agent running in a throwaway container or remote VM. This directly mitigates the exfiltration risk from the lethal trifecta <span class="citation" data-cites="LethalTrifectaAI">(Willison 2025b)</span>: private data stays out of scope by construction.</p></li>
<li><p><strong>Bisectability</strong> — deterministic builds make <code>git bisect</code> reliable. An agent can automatically bisect a regression, confident that differences in output reflect code changes rather than environment drift.</p></li>
<li><p><strong>Idempotency</strong> — reproducible workflows tend to be idempotent. Agents can safely retry failed steps, re-run the pipeline, or roll back without worrying about accumulated side effects corrupting state.</p></li>
</ol>
<div class="notes">
<p>This slide reflects personal experience rather than the assigned readings. The readings (Willison, Karpathy, DHH) consistently name testing, documentation, and version control as the preconditions for effective agentic work. Reproducibility subsumes and reinforces all three.</p>
<p>Point 1 connects to the functional programming literature. Immutability and the separation of pure and impure code reduce the surface area an agent needs to model. An agent working on a side-effect-free function is operating in a well-defined local world; an agent working on code that mutates shared state must track far more context.</p>
<p>Point 2 is particularly relevant for RSEs: research environments are often inconsistent across machines, making it hard to delegate tasks without extensive hand-holding. A reproducible environment (pixi, conda-lock, Docker, Nix) removes that friction entirely. Connecting to CI: good CI <em>is</em> a reproducible bootstrap loop, already automated and run on untrusted machines.</p>
<p>Point 3 reinforces Karpathy’s verifiability claim <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>. Reproducibility is not just a software engineering virtue; it is a precondition for the kind of feedback loop that makes agents effective. If the same code can produce different results on different runs, the agent cannot distinguish “my change broke this” from “the environment is flaky.”</p>
<p>Point 4 connects back to <span class="citation" data-cites="LethalTrifectaAI">(Willison 2025b)</span>: YOLO mode becomes much less dangerous when the agent’s access is scoped to a reproducible, self-contained project image. The agent can run destructively without touching anything real.</p>
<p>Points 5 and 6 are practical consequences rather than philosophical arguments. Worth mentioning briefly to reinforce the theme: reproducibility is not just about scientific rigour, it is an operational property that makes the agent’s job easier and safer at every step.</p>
</div>
</section>
<section id="designing-agentic-loops" class="level2">
<h2 class="anchored" data-anchor-id="designing-agentic-loops">Designing Agentic Loops</h2>
<p>An effective loop needs <span class="citation" data-cites="DesigningAgenticLoops">(Willison 2025a)</span>:</p>
<ol type="1">
<li>A <strong>clear goal</strong> with success criteria</li>
<li><strong>Tools</strong> the agent can use (shell, tests, linters, packages)</li>
<li>A <strong>feedback loop</strong> (run <img src="https://latex.codecogs.com/png.latex?%5Cto"> check <img src="https://latex.codecogs.com/png.latex?%5Cto"> iterate)</li>
</ol>
<p>Works best for: debugging, performance optimisation, dependency upgrades, refactoring — anywhere with trial-and-error and verifiable outcomes.</p>
<p>Cf. Karpathy’s verifiability thesis <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>: AI advances fastest where outputs can be <strong>verified</strong> (tests pass/fail, code compiles/crashes, benchmarks improve).</p>
<div class="notes">
<p>Willison <span class="citation" data-cites="DesigningAgenticLoops">(Willison 2025a)</span>: “Any time you find yourself thinking ‘ugh, I’m going to have to try a lot of variations here’ is a strong signal that an agentic loop might be worth trying.”</p>
<p>Karpathy’s verifiability framework complements Willison here <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>. “Traditional software automates what you can specify. LLMs automate what you can verify.” Coding is a sweet spot because feedback is immediate and unambiguous: tests pass or fail, code compiles or crashes. Karpathy also warns about “jagged intelligence” — models spike in capability where they are verifiable <em>and</em> heavily trained, and fail surprisingly in areas outside that intersection.</p>
<p>DHH’s P1 optimisation is a good concrete example of a well-designed agentic loop <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>. A senior engineer at 37signals asked: what about the fastest 1% of requests — can we make the floor faster? Took P1 from 4ms to under 0.5ms. 12 PRs, ~2,500 lines changed, done in a few days as a side project. This is work that wouldn’t have been contemplated before.</p>
<p>YOLO mode (auto-approving all agent commands) is dangerous but dramatically more effective. Willison’s mitigations <span class="citation" data-cites="DesigningAgenticLoops">(Willison 2025a)</span>: sandbox (Docker, Apple containers), run on someone else’s machine (Codespaces), or accept the risk. “Most people choose option 3.” For RSEs handling sensitive research data, the exfiltration risk deserves special attention.</p>
</div>
</section>
<section id="when-to-use-an-agent-a-personal-heuristic" class="level2">
<h2 class="anchored" data-anchor-id="when-to-use-an-agent-a-personal-heuristic">When to Use an Agent: A Personal Heuristic</h2>
<dl>
<dt>If you <strong>know</strong> how to do it</dt>
<dd>
Try it with an agent anyway. You can evaluate the output, catch mistakes, and learn to direct agents effectively.
</dd>
<dt>If you <strong>don’t know</strong> how to do it</dt>
<dd>
Don’t delegate to an agent (yet). You cannot evaluate the output — and having it done <em>for</em> you suppresses the learning you need.
</dd>
</dl>
<p>The correct moment to introduce an agent is <em>after</em> you understand the problem well enough to judge the answer.</p>
<div class="notes">
<p>The second point draws on well-replicated findings that using LLMs to answer questions — rather than struggling through them yourself — reduces retention and understanding. This is the same phenomenon Karpathy flags at a higher level: “You can outsource your thinking, but you can’t outsource your understanding” <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>.</p>
<p>The first point is a strong argument for deliberate practice with agents on familiar territory. Most RSEs sceptical of agents have never tried them on problems they already know well. Using a known problem removes the confound of “is this output right?” and lets you focus on “how do I prompt, review, and steer?” That is where the real learning happens.</p>
<p>This heuristic also reframes the on-ramp: don’t start with your hardest unsolved problem. Start with the last bug you fixed, the last function you wrote, the last refactor you did. This is how you build taste for directing agents.</p>
</div>
</section>
</section>
<section id="whats-at-stake" class="level1">
<h1>What’s at Stake</h1>
<section id="the-amplification-claim" class="level2">
<h2 class="anchored" data-anchor-id="the-amplification-claim">The Amplification Claim</h2>
<p>The consensus across readings:</p>
<blockquote class="blockquote">
<p>“AI tools amplify existing expertise.” — Willison <span class="citation" data-cites="VibeEngineeringa">(Willison 2025c)</span></p>
</blockquote>
<blockquote class="blockquote">
<p>“Vibe coding raises the floor. Agentic engineering raises the ceiling.” — Karpathy <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span></p>
</blockquote>
<p>Evidence:</p>
<ul>
<li>DHH: senior engineers at 37signals gain far more from AI tools <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span></li>
<li>Amazon: juniors can no longer ship agent-generated code without review <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span></li>
<li>Orosz survey: Builders benefit for large changes, but report <em>identity loss</em> and more AI slop to review <span class="citation" data-cites="ImpactAISoftware">(Orosz and Nilsson 2026)</span></li>
</ul>
<p>But is this a comforting just-so story that flatters senior engineers?</p>
<ul>
<li>If amplification is real, what happens to the junior pipeline?</li>
<li>Who develops expertise when the path to expertise changes?</li>
</ul>
<div class="notes">
<p>The amplification claim is central to all the readings and worth pushing on. It’s also a claim that conveniently flatters the audience most likely to be reading these articles — and sitting in this room.</p>
<p>DHH reports the same at 37signals: seniors benefit more <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>. The survey adds texture: Builders (quality-focused engineers) benefit from AI for large refactors and “quality of life” tasks that wouldn’t have been worth the time before. But they also deal with more AI-generated slop from colleagues, spend more time debugging agent output, and some report identity loss and grief <span class="citation" data-cites="ImpactAISoftware">(Orosz and Nilsson 2026)</span>.</p>
<p>The Orosz/Nilsson survey identifies three archetypes: Builders (quality-focused, benefit but also suffer), Shippers (outcome-focused, most enthusiastic, risk: faster tech debt), Coasters (can uplevel faster, risk: generating slop) <span class="citation" data-cites="ImpactAISoftware">(Orosz and Nilsson 2026)</span>. AI amplifies the tendencies that existed before.</p>
<p>The junior pipeline question is economic territory we’re not opening today, but name it as a known gap.</p>
</div>
</section>
<section id="identity-craft-and-authorship" class="level2">
<h2 class="anchored" data-anchor-id="identity-craft-and-authorship">Identity, Craft, and Authorship</h2>
<p>DHH’s guitar analogy (Lex Fridman podcast, summer 2025):</p>
<blockquote class="blockquote">
<p>The pleasure of programming is in the <em>playing</em>, not just the output.</p>
</blockquote>
<p>DHH reversed himself in six months <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>. But the feeling didn’t vanish for everyone.</p>
<p>Orosz survey: some Builders report <em>grief</em> at no longer coding by hand <span class="citation" data-cites="ImpactAISoftware">(Orosz and Nilsson 2026)</span>.</p>
<p>Karpathy <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>:</p>
<blockquote class="blockquote">
<p>“You can outsource your thinking, but you can’t outsource your understanding.”</p>
</blockquote>
<p>Open questions:</p>
<ul>
<li>If you specify, review, and own the output but don’t type it — is it still <em>your</em> code?</li>
<li>Was DHH’s “lost competence” real, or was the framing wrong?</li>
<li>Does reviewing agent output build understanding, or erode it?</li>
</ul>
<div class="notes">
<p>These questions are deliberately provocative and should fuel the discussion. The guitar analogy resonates because many RSEs chose this career partly for the craft of coding itself. DHH abandoned his own analogy within months — he now says reviewing code is where the craft lives, and insists that beauty still matters (“when something is beautiful, it’s likely to be correct”) <span class="citation" data-cites="DHHsNewWay">(Orosz 2026)</span>.</p>
<p>Karpathy’s distinction between thinking and understanding is the philosophical anchor <span class="citation" data-cites="SequoiaAscent2026">(Karpathy 2026)</span>. Agents can do the thinking (generate code, explore approaches), but the human needs understanding to direct them — know what’s worth building, what result is suspicious, what tradeoff is acceptable. His MenuGen payment bug illustrates: the agent matched Stripe emails to Google accounts to assign credits, but those can be different emails. Plausible code, bad system design. A human needs enough product and engineering judgement to catch that.</p>
<p>For RSEs specifically: if we value understanding the science behind the software, does agentic coding help or hinder that understanding? The authorship question also has practical stakes for academic credit and reproducibility.</p>
</div>
</section>
<section id="the-tastetalent-gap" class="level2">
<h2 class="anchored" data-anchor-id="the-tastetalent-gap">The Taste–Talent Gap</h2>
<p>Ira Glass’s observation: individuals acquire <em>taste</em> much faster than <em>talent</em>.</p>
<p>Coding agents narrow this gap:</p>
<ul>
<li>Taste drives the work; skill executes it — and agents now execute</li>
<li>Corollary: invest in <strong>taste</strong> (fast to acquire, high leverage) over memorising syntax (slow, increasingly low leverage)</li>
</ul>
<p>The flip side — if you lack taste or domain knowledge:</p>
<blockquote class="blockquote">
<p>You cannot judge outputs you cannot recognise as wrong.</p>
</blockquote>
<p>A deeper corollary: as AI capability surpasses human ability in a domain, the human stops <em>noticing</em> further advances — because evaluation requires being close enough to the ceiling to see it.</p>
<div class="notes">
<p>Ira Glass’s “taste-talent gap” is about the frustrating early years of creative work: your taste (what you know is good) outpaces your current ability to produce it. Coding agents invert the usual bottleneck — taste becomes the binding constraint, not raw coding ability.</p>
<p>This reframes Willison’s amplification claim <span class="citation" data-cites="VibeEngineeringa">(Willison 2025c)</span>: what gets amplified is taste and domain knowledge, not raw coding speed. A researcher with deep physical intuition who never mastered Python can now direct agents well. A competent programmer with no domain understanding cannot.</p>
<p>The evaluation ceiling problem is the converse of the amplification story: once a model’s capability in a domain exceeds the reviewer’s level, the human can no longer reliably distinguish excellent outputs from merely plausible ones. For RSEs, this is a near-term practical concern in specialised subfields — and it makes domain expertise the durable investment.</p>
</div>
</section>
<section id="economic-dimension-for-reference-not-for-discussion-today" class="level2">
<h2 class="anchored" data-anchor-id="economic-dimension-for-reference-not-for-discussion-today">Economic Dimension <em>(for reference — not for discussion today)</em></h2>
<p>Orosz &amp; Nilsson survey (900+ engineers) <span class="citation" data-cites="ImpactAISoftware">(Orosz and Nilsson 2026)</span>:</p>
<ul>
<li>Companies spending $100–200/month per engineer on AI tools</li>
<li>~30% of engineers hitting usage limits</li>
<li>Cost trajectory widely considered unsustainable</li>
<li>UK/EU companies significantly more budget-cautious than US</li>
</ul>
<p>Three archetypes:</p>
<dl>
<dt>Builders</dt>
<dd>
Care about quality and craft. Benefit from AI for large changes. Risk: identity loss, AI slop.
</dd>
<dt>Shippers</dt>
<dd>
Focus on outcomes. Most enthusiastic about AI. Risk: faster tech debt, building the wrong things.
</dd>
<dt>Coasters</dt>
<dd>
Can uplevel faster with AI. Risk: generating slop that frustrates Builders.
</dd>
</dl>
<div class="notes">
<p>This slide is reference material only. We deliberately excluded the economic dimension from today’s discussion — the readings were selected to focus on the technical, psychological, and philosophical dimensions instead. If attendees raise cost or labour-market concerns, acknowledge the omission rather than improvise.</p>
<p>The METR study (not assigned reading) found that experienced open-source developers believed they were 20% faster with AI tools while being measurably 19% slower. Worth mentioning as a provocation if anyone insists on the productivity question, but do not open it as a full discussion thread.</p>
</div>
</section>
</section>
<section id="discussion" class="level1">
<h1>Discussion</h1>
<section id="over-to-you" class="level2">
<h2 class="anchored" data-anchor-id="over-to-you">Over to You</h2>
<blockquote class="blockquote">
<p>Willison’s vibe engineering requires rigorous tests and specs to be responsible. <strong>What does responsible agentic coding look like for research software, where the test harness often doesn’t exist and “correct” is contested?</strong></p>
</blockquote>
<div class="notes">
<p>This is the bridge from the readings to the audience’s working context. Pause here and let the room sit with the question before moving to the canned questions.</p>
<p>Research software often lacks the preconditions that Willison and Karpathy assume: comprehensive test suite, clear success criteria, stable specs <span class="citation" data-cites="VibeEngineeringa SequoiaAscent2026">(Willison 2025c; Karpathy 2026)</span>. In research, “correct” might mean “agrees with physical theory,” “matches experimental data within error bars,” or “reproduces a known benchmark” — none of which are trivially automatable. The tension is sharp: the readings all agree that tests are the foundation of effective agentic coding, but RSEs often work in domains where the test harness is the unsolved problem.</p>
</div>
</section>
<section id="discussion-questions" class="level2">
<h2 class="anchored" data-anchor-id="discussion-questions">Discussion Questions</h2>
<ol type="1">
<li><p>DHH reversed his position in ~6 months. Has anyone here had a similar shift? What triggered it?</p></li>
<li><p>Willison says AI amplifies existing expertise. Does that match your experience — is there a skill AI makes <em>less</em> valuable?</p></li>
<li><p>“You can outsource your thinking, but you can’t outsource your understanding.” Where’s the line for research software?</p></li>
<li><p>If an RSE uses an agent to write code for a research project, who is responsible for the correctness of that code? Does the answer change if it produces a published result?</p></li>
<li><p>Individual RSEs may have strong AI preferences — from agent-first to AI-free — and project PIs have their own requirements driven by personal preference or funding constraints. Should RSE–project matching take these into account, so that an RSE who only works with agents is not assigned to a project that prohibits AI use, and vice versa?</p></li>
</ol>
<div class="notes">
<p>You probably won’t get to all five. Pick one or two that the room gravitates toward. Question 1 is a warm-up that also surfaces how many attendees have hands-on experience. Questions 2–3 probe the core claims. Questions 4–5 are RSE-specific and likely to generate the most productive discussion.</p>
<p>If time is short, prioritise 4 or 5. Question 4 connects to academic integrity and reproducibility — familiar ground for RSEs. Question 5 connects to practical team management — the tension between individual tool preferences and project-level constraints, which is likely to surface real experiences.</p>
</div>
</section>
<section id="non-questions-out-of-scope-today" class="level2">
<h2 class="anchored" data-anchor-id="non-questions-out-of-scope-today">Non-Questions <em>(out of scope today)</em></h2>
<ul>
<li><del>“Will AI replace RSEs?”</del> — labour-market predictions</li>
<li><del>“Which tool should we use?”</del> — tool recommendations</li>
<li><del>“Does AI actually make us faster?”</del> — empirical productivity measurement</li>
<li><del>“How do we get budget for AI tools?”</del> — procurement</li>
</ul>
<div class="notes">
<p>Naming what’s out of scope prevents drift into familiar but unproductive territory.</p>
<p>On “does AI actually make us faster”: the METR 2025 study (an RCT with experienced open-source developers) found a striking mismatch — developers believed they were 20% faster while being measurably 19% slower. Mention this if someone insists on the productivity question; do not open it as a full thread.</p>
<p>On “which tool”: tempting for a technical audience but will consume all available time and produce no actionable insight in a one-hour session.</p>
<p>These are not unimportant questions. They’re just not today’s questions.</p>
</div>
</section>
<section id="readings" class="level2">
<h2 class="anchored" data-anchor-id="readings">Readings</h2>
<ol start="0" type="1">
<li>New, optional: <a href="https://karpathy.bearblog.dev/sequoia-ascent-2026/"><span class="citation" data-cites="SequoiaAscent2026">Karpathy (<span>2026</span>)</span>’s From Vibe Coding to Agentic Engineering</a></li>
<li>Mandatory: <a href="https://simonwillison.net/2025/Oct/7/vibe-engineering/"><span class="citation" data-cites="VibeEngineeringa">Willison (<span>2025c</span>)</span>’s Vibe engineering</a></li>
<li>Mandatory: <a href="https://newsletter.pragmaticengineer.com/p/dhhs-new-way-of-writing-code"><span class="citation" data-cites="DHHsNewWay">Orosz (<span>2026</span>)</span>’s DHH’s new way of writing code</a>, which is a summary of a 2hr podcast.</li>
<li>Recommended: <a href="https://simonwillison.net/2025/Sep/30/designing-agentic-loops/"><span class="citation" data-cites="DesigningAgenticLoops">Willison (<span>2025a</span>)</span>’s Designing agentic loops</a>: good companion to (1) above.</li>
<li>Optional: <a href="https://newsletter.pragmaticengineer.com/p/the-impact-of-ai-on-software-engineers-2026"><span class="citation" data-cites="ImpactAISoftware">Orosz and Nilsson (<span>2026</span>)</span>’s The impact of AI on software engineers in 2026: key trends</a></li>
<li>Optional: <a href="https://world.hey.com/dhh/promoting-ai-agents-3ee04945"><span class="citation" data-cites="PromotingAIAgents">Hansson (<span>2026</span>)</span>’s Promoting AI agents</a>: DHH’s own (shorter) blog post making a statement similar to (2) above.</li>
</ol>
<div class="notes">
<p>Included for reference during discussion. Some attendees may have only watched Karpathy’s 30-minute video, which covers most of the conceptual framework (Software 3.0, vibe coding vs agentic engineering, the December inflection, verifiability). The slides are designed to fill in what Karpathy doesn’t cover: Willison’s specific practice framework, DHH’s personal reversal story, and the Orosz/Nilsson survey data.</p>
</div>
</section>
<section id="sequoiaascent2026" class="level2">
<h2 class="anchored" data-anchor-id="sequoiaascent2026"><span class="citation" data-cites="SequoiaAscent2026">Karpathy (2026)</span></h2>
<blockquote class="blockquote">
<p>The unit of programming changed from typing lines of code to delegating larger “macro actions”… This is why I think the profession is being refactored. The programmer is increasingly not just a code writer, but an orchestrator of agents.</p>
</blockquote>
<blockquote class="blockquote">
<p>My core automation framework is:</p>
<ul>
<li>Traditional software automates what you can&nbsp;<strong>specify</strong>.</li>
<li>LLMs and reinforcement learning automate what you can&nbsp;<strong>verify</strong>.</li>
</ul>
</blockquote>
<blockquote class="blockquote">
<p>capability spike ~= verifiability x training attention x data coverage x economic value</p>
</blockquote>
<blockquote class="blockquote">
<p>I distinguish two related but different ideas:</p>
<ul>
<li><strong>Vibe coding</strong>&nbsp;raises the floor. It lets almost anyone create software by describing what they want.</li>
<li><strong>Agentic engineering</strong>&nbsp;raises the ceiling. It is the professional discipline of coordinating fallible agents while preserving correctness, security, taste, and maintainability.</li>
</ul>
</blockquote>
<blockquote class="blockquote">
<p>The old “10x engineer” idea may become much more extreme. People who master agentic workflows may outperform others by far more than 10x.</p>
</blockquote>
<blockquote class="blockquote">
<p>This means products need agent-native surfaces… I think about this in terms of&nbsp;<strong>sensors</strong>&nbsp;and&nbsp;<strong>actuators</strong>. A sensor turns some state of the world into digital information. An actuator lets an agent change something. The future stack is agents using sensors and actuators on behalf of people and organizations.</p>
</blockquote>
<blockquote class="blockquote">
<p>The right posture is neither dismissal nor blind trust. It is empirical familiarity: learn where they work, where they fail, what they were trained for, and how to build guardrails around them.</p>
</blockquote>
<blockquote class="blockquote">
<p>You can outsource your thinking, but you can’t outsource your understanding.</p>
</blockquote>
<blockquote class="blockquote">
<p>The scarce thing is shifting:</p>
<ul>
<li>Less scarce: code generation, API recall, boilerplate, first drafts, repetitive setup, simple transformations.</li>
<li>More scarce: understanding, taste, eval design, security, system boundaries, agent orchestration, domain-specific feedback loops, and knowing when the model is off the rails.</li>
</ul>
</blockquote>
<blockquote class="blockquote">
<p>My current worldview is not that AI simply makes everyone faster at the old work. It is that the work itself is being reorganized around agents. Software, research, education, infrastructure, and knowledge work are all becoming variations of the same pattern:</p>
<ul>
<li>define the context</li>
<li>define the tools</li>
<li>define the feedback loop</li>
<li>define the guardrails</li>
<li>let agents work</li>
<li>preserve human understanding</li>
</ul>
</blockquote>
</section>
<section id="vibeengineeringa" class="level2">
<h2 class="anchored" data-anchor-id="vibeengineeringa"><span class="citation" data-cites="VibeEngineeringa">Willison (2025c)</span></h2>
<blockquote class="blockquote">
<p>I feel like&nbsp;<strong>vibe coding</strong>&nbsp;is&nbsp;<a href="https://simonwillison.net/2025/Mar/19/vibe-coding/">pretty well established now</a>&nbsp;as covering the fast, loose and irresponsible way of building software with AI—entirely prompt-driven, and with no attention paid to how the code actually works.</p>
</blockquote>
<blockquote class="blockquote">
<p>If you’re going to really exploit the capabilities of these new tools, you need to be operating&nbsp;<em>at the top of your game</em>. You’re not just responsible for writing the code—you’re researching approaches, deciding on high-level architecture, writing specifications, defining success criteria,&nbsp;<a href="https://simonwillison.net/2025/Sep/30/designing-agentic-loops/">designing agentic loops</a>, planning QA, managing a growing army of weird digital interns who will absolutely cheat if you give them a chance, and spending&nbsp;<em>so much time on code review</em>.</p>
<p>Almost all of these are characteristics of senior software engineers already!</p>
</blockquote>
</section>
<section id="dhhsnewway" class="level2">
<h2 class="anchored" data-anchor-id="dhhsnewway"><span class="citation" data-cites="DHHsNewWay">Orosz (2026)</span></h2>
<blockquote class="blockquote">
<p>A big win from using AI agents is tackling stuff that you wouldn’t have before. A senior engineer at 37signals ran a “P1 optimization” project to improve the fastest 1% of requests.</p>
</blockquote>
<blockquote class="blockquote">
<p>Running several AI agents feels less like “project management” and more like “wearing a mech suit.”</p>
</blockquote>
<blockquote class="blockquote">
<p>37signals has one designer for every two engineers.</p>
</blockquote>
<blockquote class="blockquote">
<p>AI agents could turn 37signals’ “designer model” into the industry standard.</p>
</blockquote>
<blockquote class="blockquote">
<p>Command Line Interfaces (CLI) feel like the ultimate AI interface, which validates the Unix philosophy of the 1970s.</p>
</blockquote>
<blockquote class="blockquote">
<p>Eight hours of sleep is non-negotiable – even during an AI gold rush!</p>
</blockquote>
</section>
<section id="designingagenticloops" class="level2">
<h2 class="anchored" data-anchor-id="designingagenticloops"><span class="citation" data-cites="DesigningAgenticLoops">Willison (2025a)</span></h2>
<blockquote class="blockquote">
<p>The thing to look out for here are problems with clear success criteria where finding a good solution is likely to involve (potentially slightly tedious) trial and error.</p>
</blockquote>
</section>
<section id="lethaltrifectaai" class="level2">
<h2 class="anchored" data-anchor-id="lethaltrifectaai"><span class="citation" data-cites="LethalTrifectaAI">Willison (2025b)</span></h2>
<blockquote class="blockquote">
<p>The lethal trifecta of capabilities is:</p>
<ul>
<li>Access to your private data—one of the most common purposes of tools in the first place!</li>
<li>Exposure to untrusted content—any mechanism by which text (or images) controlled by a malicious attacker could become available to your LLM</li>
<li>The ability to externally communicate in a way that could be used to steal your data (I often call this “exfiltration” but I’m not confident that term is widely understood.)</li>
</ul>
</blockquote>
</section>
<section id="references" class="level2">




</section>
</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-PromotingAIAgents" class="csl-entry">
Hansson, David Heinemeier. 2026. <span>“Promoting AI Agents.”</span> Blog. David Heinemeier Hansson, January 7. <a href="https://world.hey.com/dhh/promoting-ai-agents-3ee04945">https://world.hey.com/dhh/promoting-ai-agents-3ee04945</a>.
</div>
<div id="ref-SequoiaAscent2026" class="csl-entry">
Karpathy, Andrej. 2026. <span>“From Vibe Coding to Agentic Engineering.”</span> Blog. Bear Blog of Andrej Karpathy, April 30. <a href="https://karpathy.bearblog.dev/sequoia-ascent-2026/">https://karpathy.bearblog.dev/sequoia-ascent-2026/</a>.
</div>
<div id="ref-DHHsNewWay" class="csl-entry">
Orosz, Gergely. 2026. <span>“DHH’s New Way of Writing Code.”</span> Newsletter. The Pragmatic Engineer, April 8. <a href="https://newsletter.pragmaticengineer.com/p/dhhs-new-way-of-writing-code">https://newsletter.pragmaticengineer.com/p/dhhs-new-way-of-writing-code</a>.
</div>
<div id="ref-ImpactAISoftware" class="csl-entry">
Orosz, Gergely, and Elin Nilsson. 2026. <span>“The Impact of AI on Software Engineers in 2026: Key Trends.”</span> Newsletter. The Pragmatic Engineer, April 14. <a href="https://newsletter.pragmaticengineer.com/p/the-impact-of-ai-on-software-engineers-2026">https://newsletter.pragmaticengineer.com/p/the-impact-of-ai-on-software-engineers-2026</a>.
</div>
<div id="ref-DesigningAgenticLoops" class="csl-entry">
Willison, Simon. 2025a. <span>“Designing Agentic Loops.”</span> Blog. Simon Willison’s Weblog, September 30. <a href="https://simonwillison.net/2025/Sep/30/designing-agentic-loops/">https://simonwillison.net/2025/Sep/30/designing-agentic-loops/</a>.
</div>
<div id="ref-LethalTrifectaAI" class="csl-entry">
Willison, Simon. 2025b. <span>“The Lethal Trifecta for AI Agents: Private Data, Untrusted Content, and External Communication.”</span> Blog. Simon Willison’s Weblog, June 16. <a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/">https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/</a>.
</div>
<div id="ref-VibeEngineeringa" class="csl-entry">
Willison, Simon. 2025c. <span>“Vibe Engineering.”</span> Blog. Simon Willison’s Weblog, October 7. <a href="https://simonwillison.net/2025/Oct/7/vibe-engineering/">https://simonwillison.net/2025/Oct/7/vibe-engineering/</a>.
</div>
</div></section></div> ]]></description>
  <category>Internal presentation</category>
  <category>LLM</category>
  <category>Agentic engineering</category>
  <guid>https://blog.kolen.dev/RSE/UoE/2026-05-06-journal-club-article.html</guid>
  <pubDate>Wed, 06 May 2026 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Reproducibility in Computational Research</title>
  <dc:creator>Kolen Cheung</dc:creator>
  <link>https://blog.kolen.dev/RSE/UoE/2025-11-26-reproducibility-article.html</link>
  <description><![CDATA[ 





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><embed src="./2025-11-26-reproducibility.html" style="width:100.0%" height="400"></p>
<figcaption><a href="./2025-11-26-reproducibility.html">Slide to the talk</a></figcaption>
</figure>
</div>
<section id="introduction" class="level1">
<h1>Introduction</h1>
<section id="what-is-reproducibility" class="level2">
<h2 class="anchored" data-anchor-id="what-is-reproducibility">What is reproducibility?</h2>
<div class="columns">
<div class="column" style="width:40%;">
<p>According to <span class="citation" data-cites="hernandez_repeatability_2023">Hernández and Colom (2023)</span>,</p>
<ol type="1">
<li>Re-runnable (<img src="https://latex.codecogs.com/png.latex?R%5E1">)</li>
<li>Repeatable (<img src="https://latex.codecogs.com/png.latex?R%5E2">)</li>
<li>Reproducible (<img src="https://latex.codecogs.com/png.latex?R%5E3">)</li>
<li>Reusable (<img src="https://latex.codecogs.com/png.latex?R%5E4">)</li>
<li>Replicable (<img src="https://latex.codecogs.com/png.latex?R%5E5">)</li>
</ol>
<table class="caption-top table">
<caption>Comparison of terminologies. See <span class="citation" data-cites="plesser_reproducibility_2018">Plesser (2018)</span></caption>
<thead>
<tr class="header">
<th style="text-align: left;"><strong>Goodman</strong></th>
<th style="text-align: left;"><strong>Claerbout</strong></th>
<th style="text-align: left;"><strong>ACM</strong></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"></td>
<td style="text-align: left;"></td>
<td style="text-align: left;">Repeatability</td>
</tr>
<tr class="even">
<td style="text-align: left;">Methods reproducibility</td>
<td style="text-align: left;">Reproducibility</td>
<td style="text-align: left;">Replicability</td>
</tr>
<tr class="odd">
<td style="text-align: left;">Results reproducibility</td>
<td style="text-align: left;">Replicability</td>
<td style="text-align: left;">Reproducibility</td>
</tr>
<tr class="even">
<td style="text-align: left;">Inferential reproducibility</td>
<td style="text-align: left;"></td>
<td style="text-align: left;"></td>
</tr>
</tbody>
</table>
</div><div class="column" style="width:60%;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/dot/reproducibility.svg" class="img-fluid figure-img" style="width:100.0%"></p>
<figcaption>Different kinds of reproducibility</figcaption>
</figure>
</div>
</div>
</div>
<div class="notes">
<p>Defining reproducibility is surprisingly challenging, as the terminology varies significantly across different fields and communities. When researchers discuss reproducibility and related concepts, they may use the same words with entirely different meanings, so it is essential to be aware of whom you are speaking with. In our field, we typically focus on what might be called “methods reproducibility,” which centers on the recipe—how you take the same piece of data and use the same software to reproduce exactly the same kind of result. This contrasts with “results reproducibility,” which is more about replicating the science: given the same data, researchers are free to implement their own software to verify findings.</p>
<p>When we talk about computational reproducibility, there are actually many interconnected components. At its core, we are asking whether the same data can produce the same results. This involves not just the data itself but also the software, the workflow (ensuring that complex procedures are reproducible and produce the intended outputs), and the operational environment. Binary reproducibility—where compiling the same source code twice produces bit-for-bit identical binaries—is technically achievable but extremely difficult and, for many practical purposes, not critically important. Source reproducibility, where the same program with the same input produces exactly the same output, is also challenging because factors like floating-point arithmetic being non-associative mean that adding numbers in a different order yields different results. The focus of this discussion is primarily on environment reproducibility: how can you ensure that from a given source code, you can build the software and construct an environment (potentially including multiple software components, dependencies, and even the operating system itself) to load your data and ultimately obtain your results?</p>
</div>
</section>
<section id="what-is-a-package-manager" class="level2">
<h2 class="anchored" data-anchor-id="what-is-a-package-manager">What is a package manager?</h2>
<p><strong>“Package manager” can refer to multiple things:</strong></p>
<ul>
<li><strong>Software/tool</strong>: The CLI program (<code>conda</code>, <code>pip</code>, <code>apt</code>)
<ul>
<li>Installs, updates, resolves dependencies</li>
</ul></li>
<li><strong>Ecosystem</strong>: The collection of available packages
<ul>
<li>“Install scipy via conda” <img src="https://latex.codecogs.com/png.latex?%5CRightarrow"> scipy is <em>packaged for</em> conda</li>
</ul></li>
<li><strong>Index/channel/registry</strong>: Where packages are hosted
<ul>
<li>conda-forge, PyPI, npm registry, Debian repos</li>
</ul></li>
<li><strong>Distribution</strong> (sometimes): Bundled tool + curated packages
<ul>
<li>Anaconda = conda + default channels + selected packages</li>
</ul></li>
</ul>
<div class="notes">
<p>The term “package manager” can refer to several distinct but related concepts. First and most obviously, it refers to the software program itself—the command-line tool you invoke when you type <code>conda</code>, <code>pip</code>, <code>apt</code>, or similar commands. But when you tell someone they can use conda to install SciPy or NumPy, you are not merely describing the capability of the software; you are implicitly referring to the ecosystem in which that package has been prepared and made available. The packaging work that enables this installation is substantial.</p>
<p>Additionally, package managers involve an index (sometimes called a channel or registry). For users of systems like Ubuntu, adding a repository URL before installing certain packages is a familiar operation—you are telling the package manager where to look for the software. Finally, a package manager can sometimes refer to an entire distribution. For example, when people install Anaconda, they are getting a distribution: a bundle of the conda command-line tool, a default channel, and a curated selection of packages. Similarly, Miniforge is a distribution containing conda, mamba, the conda-forge channel, and a minimal set of packages.</p>
</div>
</section>
<section id="what-kinds-of-package-managers" class="level2">
<h2 class="anchored" data-anchor-id="what-kinds-of-package-managers">What kinds of package managers?</h2>
<p><strong>By scope:</strong></p>
<ul>
<li><strong>System-level</strong> (OS packages): apt, dnf, zypper, pacman, brew, nix</li>
<li><strong>Language/library-specific</strong>: pip (Python), npm (JavaScript), cargo (Rust), gem (Ruby), Maven (Java)</li>
<li><strong>Application bundlers</strong>: snap, flatpak, AppImage (self-contained apps with dependencies)</li>
</ul>
<p><strong>By distribution method:</strong></p>
<ul>
<li><strong>Source-based</strong>: Gentoo Portage, BSD ports, AUR (compile on your machine)</li>
<li><strong>Binary-based</strong>: apt, dnf, brew (download precompiled)</li>
<li><strong>Hybrid</strong>: Homebrew (bottles + source fallback), Nix</li>
</ul>
<p><strong>By platform:</strong></p>
<ul>
<li><strong>Single-platform</strong>: apt (Debian/Ubuntu), winget (Windows), pkg (FreeBSD)</li>
<li><strong>Cross-platform</strong>: conda, Nix, pkgsrc, Homebrew</li>
</ul>
<p><strong>By linking strategy:</strong></p>
<ul>
<li><strong>Dynamic linking</strong>: Traditional package managers (shared system libraries)</li>
<li><strong>Static/bundled</strong>: Flatpak, snap, AppImage (everything included)</li>
</ul>
<div class="notes">
<p>Package managers can be categorized in several ways. Some are specialized for operating systems—apt for Debian-based systems, for instance—and you would typically not want to use these for building your entire scientific software dependency stack. However, you would need them to install low-level, system-specific components. Others are language-specific, such as pip for Python. There is also a distinction between source-based and binary-based package managers. In the Python ecosystem, historically (and sometimes still today), packages could be distributed as source distributions, requiring the build process to occur on your local machine. Some package managers exclusively fetch source and always build locally, which can be useful—particularly in the HPC context where you want software optimized for a specific architecture. Binary-based package managers, by contrast, distribute pre-compiled packages. Platform support is another important consideration: can the package manager handle different computer architectures or operating systems? Cross-platform capability is valuable for deployment scenarios where you package your software once but deploy it across multiple environments.</p>
</div>
</section>
<section id="problem-statements" class="level2">
<h2 class="anchored" data-anchor-id="problem-statements">Problem statements</h2>
<ul>
<li>How to reproduce the environment of a bundle of research software across different systems, platforms?</li>
<li>How to customize the build process (e.g.&nbsp;optimization, vendor-provided compilers, interconnect libraries, etc.) while maintaining reproducibility?</li>
<li>How to distribute the environment?</li>
</ul>
<div class="notes">
<p>With this background established, we can now articulate the specific problem statements this discussion aims to address. First, how do we reproduce an environment containing a bundle of research software across different systems and platforms? “System” here refers to operating systems like macOS or Linux, while “platform” refers to architectures like x86-64 or ARM. Second, how do we customize the build process—for example, using vendor-provided compilers or interconnect libraries like MPI—while still maintaining reproducibility? This is particularly important in the HPC context, where we need to deploy software and want to extract maximum performance. Third, once we know how to build an environment on a particular system (whether a local computer, a workstation, or a specific HPC cluster), how do we translate that environment to another system?</p>
</div>
</section>
</section>
<section id="conda" class="level1">
<h1>Conda</h1>
<section id="the-problem-with-pypi-packages" class="level2">
<h2 class="anchored" data-anchor-id="the-problem-with-pypi-packages">The problem with PyPI packages</h2>
<p>From <a href="https://github.com/simonsobs/pixell/blob/b41248618ce92277a19a4efccadfc3b7403d67f5/setup.py">pixell/setup.py at b41248618ce92277a19a4efccadfc3b7403d67f5 · simonsobs/pixell</a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#!/usr/bin/env python</span></span>
<span id="cb1-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># -*- coding: utf-8 -*-</span></span>
<span id="cb1-3"></span>
<span id="cb1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">"""The setup script."""</span></span>
<span id="cb1-5"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> __future__ <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> print_function</span>
<span id="cb1-6"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> setuptools</span>
<span id="cb1-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> setuptools <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> find_packages</span>
<span id="cb1-8"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> distutils.errors <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> DistutilsError</span>
<span id="cb1-9"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> numpy.distutils.core <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> setup, Extension, build_ext, build_src</span>
<span id="cb1-10"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> versioneer</span>
<span id="cb1-11"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> os, sys</span>
<span id="cb1-12"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> subprocess <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> sp</span>
<span id="cb1-13"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb1-14"></span>
<span id="cb1-15">build_ext <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> build_ext.build_ext</span>
<span id="cb1-16">build_src <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> build_src.build_src</span>
<span id="cb1-17"></span>
<span id="cb1-18"></span>
<span id="cb1-19">compile_opts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {</span>
<span id="cb1-20">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#'extra_compile_args': ['-std=c99','-fopenmp', '-Wno-strict-aliasing', '-g', '-O0', '-fPIC', '-fsanitize=address', '-fsanitize=undefined'],</span></span>
<span id="cb1-21">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_compile_args'</span>: [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-std=c99'</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fopenmp'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-Wno-strict-aliasing'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-g'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-Ofast'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fPIC'</span>],</span>
<span id="cb1-22">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_f90_compile_args'</span>: [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fopenmp'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-Wno-conversion'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-Wno-tabs'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fPIC'</span>],</span>
<span id="cb1-23">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'f2py_options'</span>: [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'skip:'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'map_border'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'calc_weights'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">':'</span>],</span>
<span id="cb1-24">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_link_args'</span>: [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fopenmp'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-g'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fPIC'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fno-lto'</span>]</span>
<span id="cb1-25">    }</span>
<span id="cb1-26"></span>
<span id="cb1-27"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Set compiler options</span></span>
<span id="cb1-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Windows</span></span>
<span id="cb1-29"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> sys.platform <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'win32'</span>:</span>
<span id="cb1-30">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> DistutilsError(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Windows is not supported.'</span>)</span>
<span id="cb1-31"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elif</span> sys.platform <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'darwin'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> sys.platform <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'linux'</span>:</span>
<span id="cb1-32">    environment <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.environ</span>
<span id="cb1-33"></span>
<span id="cb1-34">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'CC'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> environment:</span>
<span id="cb1-35">        environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CC"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gcc"</span></span>
<span id="cb1-36">    </span>
<span id="cb1-37">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CXX"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> environment:</span>
<span id="cb1-38">        environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CXX"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"g++"</span></span>
<span id="cb1-39">    </span>
<span id="cb1-40">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"FC"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> environment:</span>
<span id="cb1-41">        environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"FC"</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"gfortran"</span></span>
<span id="cb1-42"></span>
<span id="cb1-43">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Now, try out our environment!</span></span>
<span id="cb1-44">    c_return <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sp.call([environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CC"</span>], <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"extra_compile_args"</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scripts/omp_hello.c"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-o"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/tmp/pixell-cc-test"</span>], env<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>environment)</span>
<span id="cb1-45"></span>
<span id="cb1-46">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> c_return <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb1-47">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">EnvironmentError</span>(</span>
<span id="cb1-48">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Your C compiler does not support the following flags, required by pixell: "</span></span>
<span id="cb1-49">            <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' '</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>join(compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_compile_args'</span>])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-50">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">". Consider setting the value of environment variable CC to a known good gcc install. "</span></span>
<span id="cb1-51">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The built-in Apple clang does not support OpenMP. Use Homebrew to install either gcc or llvm. "</span></span>
<span id="cb1-52">            <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Current value of $CC is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'CC'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">."</span>,</span>
<span id="cb1-53">        )</span>
<span id="cb1-54">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-55">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"C compiler found (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'CC'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">) and supports OpenMP."</span>)</span>
<span id="cb1-56">    </span>
<span id="cb1-57">    </span>
<span id="cb1-58">    cxx_return <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sp.call([environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"CXX"</span>], <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"extra_compile_args"</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scripts/omp_hello.c"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-o"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/tmp/pixell-cxx-test"</span>], env<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>environment)</span>
<span id="cb1-59"></span>
<span id="cb1-60">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> cxx_return <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb1-61">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">EnvironmentError</span>(</span>
<span id="cb1-62">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Your CXX compiler does not support the following flags, required by pixell: "</span></span>
<span id="cb1-63">            <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' '</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>join(compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_compile_args'</span>])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-64">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">". Consider setting the value of environment variable CXX to a known good gcc install. "</span></span>
<span id="cb1-65">             <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"The built-in Apple clang does not support OpenMP. Use Homebrew to install either gcc or llvm. "</span></span>
<span id="cb1-66">            <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Current value of $CXX is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'CXX'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">."</span>,</span>
<span id="cb1-67">        )</span>
<span id="cb1-68">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-69">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"CXX compiler found (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'CXX'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">) and supports OpenMP."</span>)</span>
<span id="cb1-70">    </span>
<span id="cb1-71">    fc_return <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sp.call([environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"FC"</span>], <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"extra_f90_compile_args"</span>], <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scripts/omp_hello.f90"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-o"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/tmp/pixell-fc-test"</span>], env<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>environment)</span>
<span id="cb1-72"></span>
<span id="cb1-73">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> fc_return <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb1-74">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">EnvironmentError</span>(</span>
<span id="cb1-75">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Your Fortran compiler does not support the following flags, required by pixell: "</span></span>
<span id="cb1-76">            <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">' '</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>join(compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_f90_compile_args'</span>])<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb1-77">            <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">". Consider setting the value of environment variable FC to a known good gfortran install."</span></span>
<span id="cb1-78">            <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Current value of $FC is </span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'FC'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">."</span>,</span>
<span id="cb1-79">        )</span>
<span id="cb1-80">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-81">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"Fortran compiler found (</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>environment[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'FC'</span>]<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">) and supports OpenMP."</span>)</span>
<span id="cb1-82"></span>
<span id="cb1-83">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Why do we remove -fPIC here?</span></span>
<span id="cb1-84">    compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_link_args'</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fopenmp'</span>]</span>
<span id="cb1-85"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-86">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">EnvironmentError</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Unknown platform. Please file an issue on GitHub."</span>)</span>
<span id="cb1-87"></span>
<span id="cb1-88"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> pip_install(package):</span>
<span id="cb1-89">    <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> pip</span>
<span id="cb1-90">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">hasattr</span>(pip, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'main'</span>):</span>
<span id="cb1-91">        pip.main([<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'install'</span>, package])</span>
<span id="cb1-92">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-93">        pip._internal.main([<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'install'</span>, package])</span>
<span id="cb1-94"></span>
<span id="cb1-95"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'README.rst'</span>) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> readme_file:</span>
<span id="cb1-96">    readme <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> readme_file.read()</span>
<span id="cb1-97"></span>
<span id="cb1-98"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">with</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">open</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'HISTORY.rst'</span>) <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> history_file:</span>
<span id="cb1-99">    history <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> history_file.read()</span>
<span id="cb1-100"></span>
<span id="cb1-101">requirements <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>  [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'numpy&gt;=1.20.0'</span>,</span>
<span id="cb1-102">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'astropy&gt;=2.0'</span>,</span>
<span id="cb1-103">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'setuptools&gt;=39'</span>,</span>
<span id="cb1-104">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'h5py&gt;=2.7'</span>,</span>
<span id="cb1-105">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'scipy&gt;=1.0'</span>,</span>
<span id="cb1-106">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'python_dateutil&gt;=2.7'</span>,</span>
<span id="cb1-107">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython&lt;3.0.4'</span>,</span>
<span id="cb1-108">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'healpy&gt;=1.13'</span>,</span>
<span id="cb1-109">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'matplotlib&gt;=2.0'</span>,</span>
<span id="cb1-110">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pyyaml&gt;=5.0'</span>,</span>
<span id="cb1-111">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Pillow&gt;=5.3.0'</span>,</span>
<span id="cb1-112">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pytest-cov&gt;=2.6'</span>,</span>
<span id="cb1-113">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'coveralls&gt;=1.5'</span>,</span>
<span id="cb1-114">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pytest&gt;=4.6'</span>,</span>
<span id="cb1-115">                 <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'ducc0&gt;=0.31.0'</span>]</span>
<span id="cb1-116"></span>
<span id="cb1-117"></span>
<span id="cb1-118">test_requirements <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pip&gt;=9.0'</span>,</span>
<span id="cb1-119">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'bumpversion&gt;=0.5'</span>,</span>
<span id="cb1-120">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'wheel&gt;=0.30'</span>,</span>
<span id="cb1-121">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'watchdog&gt;=0.8'</span>,</span>
<span id="cb1-122">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'flake8&gt;=3.5'</span>,</span>
<span id="cb1-123">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'coverage&gt;=4.5'</span>,</span>
<span id="cb1-124">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Sphinx&gt;=1.7'</span>,</span>
<span id="cb1-125">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'twine&gt;=1.10'</span>,</span>
<span id="cb1-126">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'numpy&gt;=1.20'</span>,</span>
<span id="cb1-127">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'astropy&gt;=2.0'</span>,</span>
<span id="cb1-128">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'setuptools&gt;=39.2'</span>,</span>
<span id="cb1-129">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'h5py&gt;=2.7,&lt;=2.10'</span>,</span>
<span id="cb1-130">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'scipy&gt;=1.0'</span>,</span>
<span id="cb1-131">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'python_dateutil&gt;=2.7'</span>,</span>
<span id="cb1-132">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython&lt;3.0.4'</span>,</span>
<span id="cb1-133">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'matplotlib&gt;=2.0'</span>,</span>
<span id="cb1-134">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pyyaml&gt;=5.0'</span>,</span>
<span id="cb1-135">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pytest-cov&gt;=2.6'</span>,</span>
<span id="cb1-136">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'coveralls&gt;=1.5'</span>,</span>
<span id="cb1-137">                     <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pytest&gt;=4.6'</span>]</span>
<span id="cb1-138"></span>
<span id="cb1-139"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Why are we doing this instead of allowing the environment to do this? We should just use -O3 and -fPIC.</span></span>
<span id="cb1-140">fcflags <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.getenv(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'FCFLAGS'</span>)</span>
<span id="cb1-141"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> fcflags <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">is</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> fcflags.strip() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span>:</span>
<span id="cb1-142">    fcflags <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-O3'</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-fPIC'</span>]</span>
<span id="cb1-143">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#fcflags = ['-O0','-fPIC', '-fsanitize=address', '-fsanitize=undefined']</span></span>
<span id="cb1-144"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="cb1-145">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'User supplied fortran flags: '</span>, fcflags)</span>
<span id="cb1-146">    <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'These will supersede other optimization flags.'</span>)</span>
<span id="cb1-147">    fcflags <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fcflags.split()</span>
<span id="cb1-148">    </span>
<span id="cb1-149">compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_f90_compile_args'</span>].extend(fcflags)</span>
<span id="cb1-150">compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_f77_compile_args'</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> compile_opts[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'extra_f90_compile_args'</span>]</span>
<span id="cb1-151"></span>
<span id="cb1-152"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> presrc():</span>
<span id="cb1-153">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Create f90 files for f2py.</span></span>
<span id="cb1-154">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> sp.call(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'make -C fortran'</span>, shell<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb1-155">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> DistutilsError(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Failure in the fortran source-prep step.'</span>)</span>
<span id="cb1-156">    </span>
<span id="cb1-157"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> prebuild():</span>
<span id="cb1-158">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Handle cythonization</span></span>
<span id="cb1-159">    no_cython <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> sp.call(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython --version'</span>,shell<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-160">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> no_cython:</span>
<span id="cb1-161">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb1-162">            <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Cython not found. Attempting a conda install first."</span>)</span>
<span id="cb1-163">            <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> conda.cli</span>
<span id="cb1-164">            conda.cli.main(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'conda'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'install'</span>,  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'-y'</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython'</span>)</span>
<span id="cb1-165">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span>:</span>
<span id="cb1-166">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">try</span>:</span>
<span id="cb1-167">                <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"conda install of cython failed. Attempting a pip install."</span>)</span>
<span id="cb1-168">                pip_install(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cython"</span>)</span>
<span id="cb1-169">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">except</span>:</span>
<span id="cb1-170">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> DistutilsError(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Cython not found and all attempts at installing it failed. User intervention required.'</span>)</span>
<span id="cb1-171">        </span>
<span id="cb1-172">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> sp.call(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'make -C cython'</span>,  shell<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>:</span>
<span id="cb1-173">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">raise</span> DistutilsError(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Failure in the cython pre-build step.'</span>)</span>
<span id="cb1-174"></span>
<span id="cb1-175"></span>
<span id="cb1-176"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> CustomBuild(build_ext):</span>
<span id="cb1-177">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> run(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb1-178">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Running build..."</span>)</span>
<span id="cb1-179">        prebuild()</span>
<span id="cb1-180">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Then let setuptools do its thing.</span></span>
<span id="cb1-181">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> build_ext.run(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>)</span>
<span id="cb1-182"></span>
<span id="cb1-183"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> CustomSrc(build_src):</span>
<span id="cb1-184">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> run(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb1-185">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Running src..."</span>)</span>
<span id="cb1-186">        presrc()</span>
<span id="cb1-187">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Then let setuptools do its thing.</span></span>
<span id="cb1-188">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> build_src.run(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>)</span>
<span id="cb1-189"></span>
<span id="cb1-190"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">class</span> CustomEggInfo(setuptools.command.egg_info.egg_info):</span>
<span id="cb1-191">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> run(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>):</span>
<span id="cb1-192">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Running EggInfo..."</span>)</span>
<span id="cb1-193">        presrc()</span>
<span id="cb1-194">        prebuild()</span>
<span id="cb1-195">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> setuptools.command.egg_info.egg_info.run(<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">self</span>)   </span>
<span id="cb1-196"></span>
<span id="cb1-197"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Cascade your overrides here.</span></span>
<span id="cb1-198">cmdclass <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {</span>
<span id="cb1-199">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'build_ext'</span>: CustomBuild,</span>
<span id="cb1-200">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'build_src'</span>: CustomSrc,</span>
<span id="cb1-201">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'egg_info'</span>: CustomEggInfo,</span>
<span id="cb1-202">}</span>
<span id="cb1-203">cmdclass <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> versioneer.get_cmdclass(cmdclass)</span>
<span id="cb1-204"></span>
<span id="cb1-205"></span>
<span id="cb1-206">setup(</span>
<span id="cb1-207">    author<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Simons Observatory Collaboration Analysis Library Task Force"</span>,</span>
<span id="cb1-208">    author_email<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'mathewsyriac@gmail.com'</span>,</span>
<span id="cb1-209">    classifiers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[</span>
<span id="cb1-210">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Development Status :: 2 - Pre-Alpha'</span>,</span>
<span id="cb1-211">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Intended Audience :: Developers'</span>,</span>
<span id="cb1-212">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'License :: OSI Approved :: BSD License'</span>,</span>
<span id="cb1-213">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Natural Language :: English'</span>,</span>
<span id="cb1-214">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Programming Language :: Python :: 2"</span>,</span>
<span id="cb1-215">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Programming Language :: Python :: 2.7'</span>,</span>
<span id="cb1-216">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Programming Language :: Python :: 3'</span>,</span>
<span id="cb1-217">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Programming Language :: Python :: 3.4'</span>,</span>
<span id="cb1-218">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Programming Language :: Python :: 3.5'</span>,</span>
<span id="cb1-219">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Programming Language :: Python :: 3.6'</span>,</span>
<span id="cb1-220">    ],</span>
<span id="cb1-221">    description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pixell"</span>,</span>
<span id="cb1-222">    package_dir<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pixell"</span>: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pixell"</span>},</span>
<span id="cb1-223">    entry_points<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{</span>
<span id="cb1-224">    },</span>
<span id="cb1-225">    ext_modules<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[</span>
<span id="cb1-226">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell.cmisc'</span>,</span>
<span id="cb1-227">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython/cmisc.c'</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython/cmisc_core.c'</span>],</span>
<span id="cb1-228">            libraries<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'m'</span>],</span>
<span id="cb1-229">            include_dirs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[np.get_include()],</span>
<span id="cb1-230">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-231">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell.distances'</span>,</span>
<span id="cb1-232">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython/distances.c'</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython/distances_core.c'</span>],</span>
<span id="cb1-233">            libraries<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'m'</span>],</span>
<span id="cb1-234">            include_dirs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[np.get_include()],</span>
<span id="cb1-235">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-236">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell.srcsim'</span>,</span>
<span id="cb1-237">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython/srcsim.c'</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cython/srcsim_core.c'</span>],</span>
<span id="cb1-238">            libraries<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'m'</span>],</span>
<span id="cb1-239">            include_dirs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[np.get_include()],</span>
<span id="cb1-240">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-241">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell._interpol_32'</span>,</span>
<span id="cb1-242">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'fortran/interpol_32.f90'</span>],</span>
<span id="cb1-243">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-244">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell._interpol_64'</span>,</span>
<span id="cb1-245">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'fortran/interpol_64.f90'</span>],</span>
<span id="cb1-246">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-247">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell._colorize'</span>,</span>
<span id="cb1-248">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'fortran/colorize.f90'</span>],</span>
<span id="cb1-249">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-250">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell._array_ops_32'</span>,</span>
<span id="cb1-251">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'fortran/array_ops_32.f90'</span>],</span>
<span id="cb1-252">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-253">        Extension(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell._array_ops_64'</span>,</span>
<span id="cb1-254">            sources<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'fortran/array_ops_64.f90'</span>],</span>
<span id="cb1-255">            <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span>compile_opts),</span>
<span id="cb1-256">    ],</span>
<span id="cb1-257">    include_dirs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [],</span>
<span id="cb1-258">    library_dirs <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> [],</span>
<span id="cb1-259">    install_requires<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>requirements,</span>
<span id="cb1-260">    extras_require <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> {<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'fftw'</span>:[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pyFFTW&gt;=0.10'</span>],<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'mpi'</span>:[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'mpi4py&gt;=2.0'</span>]},</span>
<span id="cb1-261">    license<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"BSD license"</span>,</span>
<span id="cb1-262">    long_description<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>readme <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> history,</span>
<span id="cb1-263">    package_data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>{<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell'</span>: [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell/tests/data/*.fits'</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell/tests/data/*.dat'</span>,<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell/tests/data/*.pkl'</span>]},</span>
<span id="cb1-264">    include_package_data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>,    </span>
<span id="cb1-265">    data_files<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell'</span>, [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell/arial.ttf'</span>])],</span>
<span id="cb1-266">    keywords<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell'</span>,</span>
<span id="cb1-267">    name<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell'</span>,</span>
<span id="cb1-268">    packages<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>find_packages(),</span>
<span id="cb1-269">    test_suite<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pixell.tests'</span>,</span>
<span id="cb1-270">    tests_require<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>test_requirements,</span>
<span id="cb1-271">    url<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'https://github.com/simonsobs/pixell'</span>,</span>
<span id="cb1-272">    version<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>versioneer.get_version(),</span>
<span id="cb1-273">    zip_safe<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">False</span>,</span>
<span id="cb1-274">    cmdclass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>cmdclass,</span>
<span id="cb1-275">    scripts<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'scripts/test-pixell'</span>]</span>
<span id="cb1-276">)</span>
<span id="cb1-277"></span>
<span id="cb1-278"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">\n</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">[setup.py request was successful.]'</span>)</span></code></pre></div></div>
<div class="notes">
<p>This example from the pixell package illustrates the problems inherent in PyPI packages. First, note that this is a <code>setup.py</code> file, which is notorious and increasingly deprecated—the package has since moved away from this approach. But this serves as a useful illustration of how problematic things can become. The script checks whether it is running on Windows (which it does not support), and on Darwin (macOS) or Linux, it attempts to infer what compiler is available on the system. At various points in the script’s history, it would list executables in different paths like <code>/usr/bin</code> or <code>/usr/local/bin</code> and try to locate compilers such as Fortran compilers. If unsuccessful, it would search other paths. This approach is inherently unstable and completely non-reproducible. If someone can install this package on their computer, there is no guarantee that it will successfully compile on another computer. The error messages in such cases can be extremely cryptic and difficult to debug.</p>
</div>
</section>
<section id="the-necessity-of-conda" class="level2">
<h2 class="anchored" data-anchor-id="the-necessity-of-conda">The necessity of conda</h2>
<blockquote class="blockquote">
<p>it really sounds like your needs are so unusual compared to the larger Python community that you’re just better off building your own</p>
</blockquote>
<p>From <a href="https://www.youtube.com/watch?v=QjXJLVINsSA&amp;feature=youtu.be&amp;t=3555">2012 PyData Workshop Panel Discussion with Guido van Rossum</a>. See <a href="https://jakevdp.github.io/blog/2016/08/25/conda-myths-and-misconceptions/">Conda: Myths and Misconceptions | Pythonic Perambulations</a>.</p>
<div class="notes">
<p>This quote comes from Guido van Rossum himself at a 2012 meeting. The statement essentially acknowledges that scientific computing needs are so unusual compared to the larger Python community that building a specialized package manager makes sense. This was the genesis of conda. The developers had been experiencing numerous build problems with complex scientific software—packages involving C compilers, Fortran compilers, and intricate linking requirements. The complexity became so overwhelming that this statement became the catalyst for creating conda.</p>
</div>
</section>
<section id="the-conda-solution" class="level2">
<h2 class="anchored" data-anchor-id="the-conda-solution">The conda solution</h2>
<div class="columns">
<div class="column" style="width:50%;">
<table class="caption-top table">
<caption>Separation of build, host, run-time dependencies</caption>
<colgroup>
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Section</th>
<th style="text-align: left;">Who needs it?</th>
<th style="text-align: left;">Architecture?</th>
<th style="text-align: left;">Example</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><strong>Build</strong></td>
<td style="text-align: left;">The compiler machine</td>
<td style="text-align: left;">Build Platform (e.g., x86)</td>
<td style="text-align: left;"><code>cmake</code>, <code>gcc</code>, <code>make</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Host</strong></td>
<td style="text-align: left;">The package being built (linking phase)</td>
<td style="text-align: left;">Target Platform (e.g., ARM64)</td>
<td style="text-align: left;"><code>openssl</code>, <code>python</code>, <code>libpng</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Run</strong></td>
<td style="text-align: left;">The final user</td>
<td style="text-align: left;">Target Platform (e.g., ARM64)</td>
<td style="text-align: left;"><code>python</code>, <code>requests</code>, <code>numpy</code></td>
</tr>
</tbody>
</table>
</div><div class="column" style="width:25%;">
<p>Multi-platform</p>
<ul>
<li><code>Linux-x86_64</code></li>
<li><code>Linux-aarch64</code></li>
<li><code>Linux-ppc64le</code></li>
<li><code>MacOSX-x86_64</code></li>
<li><code>MacOSX-arm64</code></li>
<li><code>Windows-x86_64</code></li>
</ul>
</div><div class="column" style="width:25%;">
<p>Language agnostic</p>
<ul>
<li>Python</li>
<li>C/C++</li>
<li>Fortran</li>
<li>R</li>
<li>Rust</li>
<li>bash</li>
<li><code>juliaup</code></li>
<li>…</li>
</ul>
</div>
</div>
<div class="notes">
<p>The conda solution addresses many of these challenges. One key aspect is the separation of build dependencies, which isolates your environment so that builds become more reproducible and less dependent on the host machine. This provides a higher degree of reproducibility: if you install something on your computer and it runs, there is a high probability it will work on another computer as well. It is not guaranteed, but the probability is significantly higher than with traditional approaches. The concept is sometimes called a “hermetic build”—the ability to bootstrap everything from scratch. Conda is close to achieving this, though not 100% hermetic. This is a useful property for achieving reproducibility.</p>
<p>Conda supports many platforms, though notably not all—OpenBSD and FreeBSD, for example, are not supported. From the ground up, conda was designed to be language-agnostic. While it is primarily associated with Python packages, you can use it to package anything: C/C++, Fortran, R, Rust, bash scripts, and even Julia (via juliaup). Though it should be noted that Julia’s packaging situation through conda is not ideal, this is not conda’s fault.</p>
</div>
</section>
<section id="conda-vs.-mamba-conda-forge" class="level2">
<h2 class="anchored" data-anchor-id="conda-vs.-mamba-conda-forge">Conda vs.&nbsp;Mamba + conda-forge</h2>
<ul>
<li>Mamba as software
<ul>
<li>mamba started as a new solver (borrowed from RHEL) to overcome the performance problem of the conda SAT solver implemented in Python</li>
<li>has fully matured, almost drop-in replacement of conda</li>
</ul></li>
<li>the conda-forge channel
<ul>
<li>the default channel in conda/mamba points to the channel (repository, index) by Anaconda. These packages are built and maintained by Anaconda</li>
<li>anyone can create new channels hosted on anaconda.org, the most prominent one being conda-forge</li>
<li>conda-forge is many things
<ul>
<li>GitHub Organization with many repos: feedstocks</li>
<li>each feedstock contains <code>meta.yaml</code> at minimum to define the packages</li>
<li>CI is deployed (with the infrastructure including bots defined by conda-forge) to build the same package across platforms and Python versions and possibly any variants (e.g.&nbsp;MPI backend)</li>
</ul></li>
</ul></li>
<li>miniforge: a distribution containing a minimal set of software including <code>conda</code>, <code>mamba</code>, <code>python</code>, etc. This is similar to the Anaconda distribution or mini-conda distribution.</li>
<li>micromamba: a statically linked version of mamba that does a subset of what mamba does. Recommendation: don’t use it unless you deploy it in CI</li>
<li>packages in conda-forge are better packaged in general
<ul>
<li>e.g.&nbsp;different BLAS and MPI variants</li>
</ul></li>
</ul>
<div class="notes">
<p>There is some potential confusion around the terminology in this ecosystem. Mamba started as a new solver borrowed from Red Hat Enterprise Linux to overcome the performance problems of conda’s SAT solver, which was implemented in Python. The original conda solver works correctly but is very slow—as people built larger and larger environments, solving could take hours. Mamba has now fully matured into an almost drop-in replacement for conda, and its solver has been merged back into conda, so the default conda solver is now also fast. Personally, I now only use mamba.</p>
<p>The default channel in conda/mamba points to packages built and maintained by Anaconda. However, anyone can create new channels hosted on anaconda.org. The most prominent community channel is conda-forge, which is many things at once: it is a GitHub organization with many repositories called feedstocks. Each feedstock contains at minimum a <code>meta.yaml</code> file that defines how to build the package. Continuous integration is deployed using infrastructure defined by conda-forge to build the same package across platforms, Python versions, and various variants (such as different MPI backends). This is a large and useful ecosystem, and for many scientific packages, conda-forge does a better job of packaging than the alternatives.</p>
</div>
</section>
<section id="how-condamamba-achieves-reproducibility" class="level2">
<h2 class="anchored" data-anchor-id="how-condamamba-achieves-reproducibility">How conda/mamba achieves reproducibility</h2>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>a package distributed via conda-forge has a strong guarantee of reproducibility by its design
<ul>
<li>conda/mamba specific design decisions</li>
<li>conda-forge specific design in infrastructure and CI</li>
</ul></li>
</ul>
</div><div class="column" style="width:50%;">
<table class="caption-top table">
<caption>Separation of build, host, run-time dependencies</caption>
<colgroup>
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
<col style="width: 25%">
</colgroup>
<thead>
<tr class="header">
<th style="text-align: left;">Section</th>
<th style="text-align: left;">Who needs it?</th>
<th style="text-align: left;">Architecture?</th>
<th style="text-align: left;">Example</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: left;"><strong>Build</strong></td>
<td style="text-align: left;">The compiler machine</td>
<td style="text-align: left;">Build Platform (e.g., x86)</td>
<td style="text-align: left;"><code>cmake</code>, <code>gcc</code>, <code>make</code></td>
</tr>
<tr class="even">
<td style="text-align: left;"><strong>Host</strong></td>
<td style="text-align: left;">The package being built (linking phase)</td>
<td style="text-align: left;">Target Platform (e.g., ARM64)</td>
<td style="text-align: left;"><code>openssl</code>, <code>python</code>, <code>libpng</code></td>
</tr>
<tr class="odd">
<td style="text-align: left;"><strong>Run</strong></td>
<td style="text-align: left;">The final user</td>
<td style="text-align: left;">Target Platform (e.g., ARM64)</td>
<td style="text-align: left;"><code>python</code>, <code>requests</code>, <code>numpy</code></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="notes">
<p>If you manage to package your software for conda-forge, it achieves a high degree of scalability and reproducibility. This in itself is a valuable exercise. Personally, when releasing a package, I always release it on PyPI first—partly to claim the namespace, as this is the authoritative source for how people find Python packages. But the next step is to distribute it via conda in some form. Packaging for conda-forge is harder than for PyPI, but this difficulty is the cost of guarantees. The separation of build, host, and runtime dependencies ensures clean builds across different platforms. The CI system is particularly useful because it operates in isolated environments independent of your local computer, testing your package on various systems you might not otherwise have access to.</p>
</div>
</section>
<section id="how-condamambaconda-forge-achieves-customizability" class="level2">
<h2 class="anchored" data-anchor-id="how-condamambaconda-forge-achieves-customizability">How conda/mamba+conda-forge achieves customizability</h2>
<p>Quoting directly from <a href="https://conda-forge.org/docs/maintainer/knowledge_base/#switching-blas-implementation">Knowledge Base | conda-forge | community-driven packaging for conda</a></p>
<p>You can switch your BLAS implementation by doing,</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">conda</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"libblas=*=*_mkl"</span></span>
<span id="cb2-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">conda</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"libblas=*=*_openblas"</span></span>
<span id="cb2-3"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">conda</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"libblas=*=*_blis"</span></span>
<span id="cb2-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">conda</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"libblas=*=*_accelerate"</span></span>
<span id="cb2-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">conda</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"libblas=*=*_newaccelerate"</span></span>
<span id="cb2-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">conda</span> install <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"libblas=*=*_netlib"</span></span></code></pre></div></div>
<p>MPI:</p>
<ul>
<li>mpi variants can be explicitly requested with <code>pkg=*=mpi_{{ mpi }}_*</code></li>
<li>any mpi variant, ignoring provider, can be requested with <code>pkg=*=mpi_*</code></li>
<li>nompi variant can be explicitly requested with <code>pkg=*=nompi_*</code></li>
</ul>
<p>Or even microarch! See <a href="https://conda-forge.org/docs/maintainer/knowledge_base/#microarch">Microarchitecture-optimized builds</a></p>
<div class="notes">
<p>This addresses the second problem statement regarding customization. You can choose different BLAS implementations—the build matrix is quite large for some packages. By default on x86-64, you might get MKL, but you can also select OpenBLAS, BLIS, Accelerate (on macOS), or Netlib. Different MPI implementations are also available: OpenMPI, MPICH, or no MPI at all. The reason for the “no MPI” variant is that MPI libraries can be difficult to run in certain situations, so if you only need serial execution, avoiding MPI dependencies simplifies things.</p>
<p>Even microarchitecture optimization is now possible. On x86-64, there are officially four different levels of microarchitecture, each with different instruction sets and vectorization widths. AVX-512, for example, uses 512-bit vectors, so with 64-bit floating-point numbers you can process eight operations concurrently in one cycle. Optimization per microarchitecture is important for squeezing out the maximum FLOPS from your CPU, and conda-forge now provides this level of control.</p>
</div>
</section>
<section id="how-to-distribute-a-condamamba-environment" class="level2">
<h2 class="anchored" data-anchor-id="how-to-distribute-a-condamamba-environment">How to distribute a conda/mamba environment</h2>
<p>Conda/mamba environment is designed to be reproducible with a different prefix (see the placeholder trick in <a href="https://mamba.readthedocs.io/en/latest/advanced_usage/detailed_operations.html#the-package-installation-process">Detailed operations — documentation</a>)</p>
<ul>
<li><p>To reproduce an environment</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># this is analogous to a lockfile</span></span>
<span id="cb3-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">mamba</span> env export <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> environment.yml</span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># this will reproduce exactly the same environment on machine with the same architecture</span></span>
<span id="cb3-4"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">mamba</span> env create <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-f</span> environment.yml</span></code></pre></div></div></li>
<li><p>Create an environment (env2) as a clone of an existing environment (env1):</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">conda</span> create <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-n</span> env2 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--clone</span> path/to/file/env1</span></code></pre></div></div></li>
</ul>
<div class="notes">
<p>The last problem statement concerns distributing environments across systems. The most rudimentary approach involves exporting an environment to a YAML file—essentially a lock file—and then creating an environment from that file on another system. In some cases, you can reproduce exactly the same environment, though platform differences may prevent this. For example, if the exported file includes MKL libraries (which are x86-64 specific), attempting to recreate the environment on ARM would fail. But within the same architecture (such as Linux x86-64), this approach works reliably.</p>
<p>Cloning environments is another option that might seem trivial—just copying a directory to another path. However, if you understand prefix paths and how they work, simply moving a directory would break your environment. Conda employs a “relocatable” trick that allows environments to work with different prefixes, which is quite useful.</p>
</div>
</section>
<section id="example-how-to-package-a-pure-python-package-thats-already-on-pypi" class="level2">
<h2 class="anchored" data-anchor-id="example-how-to-package-a-pure-python-package-thats-already-on-pypi">Example: how to package a pure Python package that’s already on PyPI</h2>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">grayskull</span> pypi pytest</span></code></pre></div></div>
<p>See more in <a href="https://github.com/conda/grayskull?tab=readme-ov-file#usage">conda/grayskull: Grayskull - Recipe generator for Conda</a>.</p>
<div class="notes">
<p>For simple cases, particularly pure Python projects that are already on PyPI, there is a tool called Grayskull that can generate a conda recipe automatically. It will create a recipe for you that is not 100% correct all the time—sometimes minor modifications are needed—but this is as good as it gets for simple projects. For pure Python packages, it often just works out of the box.</p>
</div>
</section>
<section id="example-how-to-package-a-complex-scientific-software" class="level2">
<h2 class="anchored" data-anchor-id="example-how-to-package-a-complex-scientific-software">Example: how to package a complex scientific software</h2>
<!-- Find examples from https://github.com/conda-forge/staged-recipes/pulls?q=is%3Apr+author%3Aickc -->
<p>From <a href="https://github.com/conda-forge/ducc0-feedstock/blob/f114fdaa2eb46afb5dee0c4c92b366a506f3475a/recipe/meta.yaml">ducc0-feedstock/recipe/meta.yaml at f114fdaa2eb46afb5dee0c4c92b366a506f3475a · conda-forge/ducc0-feedstock</a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode yml code-with-copy"><code class="sourceCode yaml"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% set name = </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ducc0"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb6-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% set version = </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.39.1"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb6-3"></span>
<span id="cb6-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">package</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ name|lower </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb6-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ version </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb6-7"></span>
<span id="cb6-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">url</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> https://pypi.org/packages/source/{{ name[0] }}/{{ name }}/ducc0-{{ version }}.tar.gz</span></span>
<span id="cb6-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sha256</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> 38eda188733d43c3602726e28bc9928d3117cdc23b5c1e7d89fdc26004a1d847</span></span>
<span id="cb6-11"></span>
<span id="cb6-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">build</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">number</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span></span>
<span id="cb6-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">skip</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [py&lt;=36]</span></span>
<span id="cb6-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">script_env</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> DUCC0_OPTIMIZATION=portable</span></span>
<span id="cb6-16"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">script</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ PYTHON </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">} -m pip install . -vv</span></span>
<span id="cb6-17"></span>
<span id="cb6-18"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">requirements</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">build</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> python</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">                                 # [build_platform != target_platform]</span></span>
<span id="cb6-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> cross-python_{{ target_platform }}</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">     # [build_platform != target_platform]</span></span>
<span id="cb6-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pybind11</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">                               # [build_platform != target_platform]</span></span>
<span id="cb6-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> nanobind</span></span>
<span id="cb6-24"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> make</span></span>
<span id="cb6-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> cmake</span></span>
<span id="cb6-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ compiler(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'c'</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb6-27"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ stdlib(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"c"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb6-28"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ compiler(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cxx'</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb6-29"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">host</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pip</span></span>
<span id="cb6-31"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pybind11</span></span>
<span id="cb6-32"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> nanobind</span></span>
<span id="cb6-33"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> python</span></span>
<span id="cb6-34"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> make</span></span>
<span id="cb6-35"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> cmake</span></span>
<span id="cb6-36"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> scikit-build</span></span>
<span id="cb6-37"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> scikit-build-core</span></span>
<span id="cb6-38"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-39"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> numpy &gt;=1.17.0</span></span>
<span id="cb6-40"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> python</span></span>
<span id="cb6-41"></span>
<span id="cb6-42"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">test</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-43"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">imports</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-44"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ducc0</span></span>
<span id="cb6-45"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">commands</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-46"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pip check</span></span>
<span id="cb6-47"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">requires</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-48"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pip</span></span>
<span id="cb6-49"></span>
<span id="cb6-50"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">about</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-51"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">home</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> https://gitlab.mpcdf.mpg.de/mtr/ducc</span></span>
<span id="cb6-52"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summary</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Distinctly useful code collection</span></span>
<span id="cb6-53"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">license</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> GPL-2.0-or-later</span></span>
<span id="cb6-54"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">license_file</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> LICENSE</span></span>
<span id="cb6-55"></span>
<span id="cb6-56"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">extra</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-57"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">recipe-maintainers</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-58"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ickc</span></span>
<span id="cb6-59"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> MarkWieczorek</span></span>
<span id="cb6-60"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> mreineck</span></span></code></pre></div></div>
<div class="notes">
<p>This example from the ducc0 feedstock (a CMB-related scientific package) demonstrates more complex packaging. It shows the concrete implementation of concepts discussed earlier: the separation of build, host, and runtime environments. Writing these recipes can be challenging, especially when starting out—you might encounter cryptic errors and need to experiment. The CI allows you to test your package on conda-forge’s infrastructure. Most packages include simple tests like importing the module successfully, which is not a full test suite but provides basic validation that the build succeeded.</p>
</div>
</section>
<section id="example-how-to-package-a-complex-scientific-software-contd" class="level2">
<h2 class="anchored" data-anchor-id="example-how-to-package-a-complex-scientific-software-contd">Example: how to package a complex scientific software (cont’d)</h2>
<p>From <a href="https://github.com/conda-forge/toast-feedstock/blob/df31bdbcae76b144ab89a8150ab8c43fb9a61d54/recipe/meta.yaml">toast-feedstock/recipe/meta.yaml at df31bdbcae76b144ab89a8150ab8c43fb9a61d54 · conda-forge/toast-feedstock</a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode yml code-with-copy"><code class="sourceCode yaml"><span id="cb7-1"></span>
<span id="cb7-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% set version = </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2.3.14"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb7-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% set sha256 = </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"924912213af3bbacd622b9318bd6d79055c4d57f58c2da486f4b3f62a12466f1"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb7-4"></span>
<span id="cb7-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% set build = 2 %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb7-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% if blas_impl == </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'openblas'</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb7-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% set build = build + 100 %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb7-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% endif %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb7-9"></span>
<span id="cb7-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">% set blas_prefix = blas_impl %</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb7-11"></span>
<span id="cb7-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">package</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> toast</span></span>
<span id="cb7-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ version </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-15"></span>
<span id="cb7-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">url</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> https://github.com/hpc4cmb/toast/archive/{{ version }}.tar.gz</span></span>
<span id="cb7-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">sha256</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ sha256 </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-19"></span>
<span id="cb7-20"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">build</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">skip</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">True</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [py&lt;37]</span></span>
<span id="cb7-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">skip</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">True</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [win]</span></span>
<span id="cb7-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">number</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ build </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-24"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"{{ blas_prefix }}_py{{ py }}h{{ PKG_HASH }}_{{ build }}"</span></span>
<span id="cb7-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">run_exports</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> toast * {{ blas_prefix }}_*</span></span>
<span id="cb7-27"></span>
<span id="cb7-28"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">requirements</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-29"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">build</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ compiler(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'c'</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-31"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ compiler(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'cxx'</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-32"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> cmake</span></span>
<span id="cb7-33"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> make</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">                 # [unix]</span></span>
<span id="cb7-34"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> llvm-openmp &gt;=4.0.1</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [osx]</span></span>
<span id="cb7-35"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">host</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-36"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> llvm-openmp &gt;=4.0.1</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [osx]</span></span>
<span id="cb7-37"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> python</span></span>
<span id="cb7-38"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> fftw</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [blas_impl == 'openblas']</span></span>
<span id="cb7-39"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> openblas * openmp_*</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [blas_impl == 'openblas']</span></span>
<span id="cb7-40"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> mkl-devel</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [blas_impl == 'mkl']</span></span>
<span id="cb7-41"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> liblapack</span></span>
<span id="cb7-42"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> suitesparse</span></span>
<span id="cb7-43"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> libaatm</span></span>
<span id="cb7-44"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-45"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> llvm-openmp &gt;=4.0.1</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [osx]</span></span>
<span id="cb7-46"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> python</span></span>
<span id="cb7-47"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ pin_compatible(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fftw"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [blas_impl == 'openblas']</span></span>
<span id="cb7-48"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> openblas * openmp_*</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [blas_impl == 'openblas']</span></span>
<span id="cb7-49"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ pin_compatible(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mkl"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # [blas_impl == 'mkl']</span></span>
<span id="cb7-50"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ pin_compatible(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"liblapack"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-51"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ pin_compatible(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"suitesparse"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-52"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">{ pin_compatible(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"libaatm"</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">) </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb7-53"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> numpy</span></span>
<span id="cb7-54"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> scipy</span></span>
<span id="cb7-55"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> astropy</span></span>
<span id="cb7-56"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> healpy</span></span>
<span id="cb7-57"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> h5py</span></span>
<span id="cb7-58"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ephem</span></span>
<span id="cb7-59"></span>
<span id="cb7-60"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">test</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-61"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">files</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-62"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> run_test.sh</span></span>
<span id="cb7-63"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">commands</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-64"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ./run_test.sh</span></span>
<span id="cb7-65"></span>
<span id="cb7-66"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">about</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-67"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">home</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> https://github.com/hpc4cmb/toast</span></span>
<span id="cb7-68"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">license</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> BSD-2-Clause</span></span>
<span id="cb7-69"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">license_family</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> BSD</span></span>
<span id="cb7-70"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">license_file</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> LICENSE</span></span>
<span id="cb7-71"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summary</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Time Ordered Astrophysics Scalable Tools'</span></span>
<span id="cb7-72"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">  description</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">: </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">|</span></span>
<span id="cb7-73">    TOAST is a software framework for simulating and processing timestream data</span>
<span id="cb7-74">    collected by microwave telescopes.</span>
<span id="cb7-75"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dev_url</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> https://github.com/hpc4cmb/toast</span></span>
<span id="cb7-76"></span>
<span id="cb7-77"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">extra</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-78"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">recipe-maintainers</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb7-79"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> tskisner</span></span></code></pre></div></div>
<div class="notes">
<p>This even more complex example from the TOAST feedstock demonstrates how to pin to compiler components without specifying implementation details—dispatching between clang and LLVM, for instance. This is a template defined by conda-forge, not native to conda itself. You can see conditional logic embedded in what appear to be YAML comments (using Jinja2 templating). The recipe handles different BLAS implementations and MPI variants, showing how the build matrix can become quite sophisticated for packages with many optional dependencies.</p>
</div>
</section>
<section id="example-how-to-reproduce-a-set-of-system-softwares-on-hpc" class="level2">
<h2 class="anchored" data-anchor-id="example-how-to-reproduce-a-set-of-system-softwares-on-hpc">Example: how to reproduce a set of system softwares on HPC</h2>
<p>From <a href="https://github.com/ickc/bootstrapping-os-environments/blob/main/conda/system/pixi.toml">bootstrapping-os-environments/conda/system/pixi.toml at main · ickc/bootstrapping-os-environments</a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode yml code-with-copy"><code class="sourceCode yaml"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">channels</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb8-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> conda-forge</span></span>
<span id="cb8-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dependencies</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb8-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bash</span></span>
<span id="cb8-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bat</span></span>
<span id="cb8-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bat-extras</span></span>
<span id="cb8-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bottom</span></span>
<span id="cb8-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> btop</span></span>
<span id="cb8-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bzip2</span></span>
<span id="cb8-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> clang-format</span></span>
<span id="cb8-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> coreutils</span></span>
<span id="cb8-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> curl</span></span>
<span id="cb8-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> difftastic</span></span>
<span id="cb8-14"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> diffutils</span></span>
<span id="cb8-15"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> direnv</span></span>
<span id="cb8-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> dua-cli</span></span>
<span id="cb8-17"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> dust</span></span>
<span id="cb8-18"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> exiftool</span></span>
<span id="cb8-19"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> fastfetch</span></span>
<span id="cb8-20"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> fd-find</span></span>
<span id="cb8-21"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ffmpeg</span></span>
<span id="cb8-22"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> file</span></span>
<span id="cb8-23"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> findutils</span></span>
<span id="cb8-24"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> fzf</span></span>
<span id="cb8-25"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> gawk</span></span>
<span id="cb8-26"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> gh</span></span>
<span id="cb8-27"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ghostscript</span></span>
<span id="cb8-28"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> git</span></span>
<span id="cb8-29"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> git-delta</span></span>
<span id="cb8-30"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> gnu-units</span></span>
<span id="cb8-31"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> go-shfmt</span></span>
<span id="cb8-32"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> go-task</span></span>
<span id="cb8-33"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> graphviz</span></span>
<span id="cb8-34"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> grep</span></span>
<span id="cb8-35"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> gzip</span></span>
<span id="cb8-36"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> htop</span></span>
<span id="cb8-37"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> hyperfine</span></span>
<span id="cb8-38"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> imagemagick</span></span>
<span id="cb8-39"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> inetutils</span></span>
<span id="cb8-40"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> joshuto</span></span>
<span id="cb8-41"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> jq</span></span>
<span id="cb8-42"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> juliaup</span></span>
<span id="cb8-43"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> libarchive</span></span>
<span id="cb8-44"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> lsdeluxe</span></span>
<span id="cb8-45"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> make</span></span>
<span id="cb8-46"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> mediainfo</span></span>
<span id="cb8-47"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> mosh</span></span>
<span id="cb8-48"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> nano</span></span>
<span id="cb8-49"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> nvtop</span></span>
<span id="cb8-50"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> onefetch</span></span>
<span id="cb8-51"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> openssh</span></span>
<span id="cb8-52"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pandoc</span></span>
<span id="cb8-53"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> parallel</span></span>
<span id="cb8-54"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> patch</span></span>
<span id="cb8-55"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pdf2svg</span></span>
<span id="cb8-56"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pixi</span></span>
<span id="cb8-57"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> poppler</span></span>
<span id="cb8-58"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> prettier</span></span>
<span id="cb8-59"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ripgrep</span></span>
<span id="cb8-60"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> rsync</span></span>
<span id="cb8-61"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> sed</span></span>
<span id="cb8-62"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> shellcheck</span></span>
<span id="cb8-63"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> starship</span></span>
<span id="cb8-64"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> tar</span></span>
<span id="cb8-65"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> tmux</span></span>
<span id="cb8-66"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> tokei</span></span>
<span id="cb8-67"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> tree</span></span>
<span id="cb8-68"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> unzip</span></span>
<span id="cb8-69"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> uv</span></span>
<span id="cb8-70"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> wget</span></span>
<span id="cb8-71"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> which</span></span>
<span id="cb8-72"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> zellij</span></span>
<span id="cb8-73"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> zsh</span></span>
<span id="cb8-74"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> zstd</span></span>
<span id="cb8-75"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> system</span></span></code></pre></div></div>
<div class="notes">
<p>This example addresses how to reproduce a set of system software on HPC. Consider tools like fzf, fd-find, curl, bat, ripgrep, and others that you might want available on any system you work on. In the past, HPC environments were often quite minimal, lacking many common utilities. I have experimented extensively with different methods of installing command-line tools in user home directories on Linux—from source compilation to Gentoo’s Portage—but this approach is the best I have found. It leverages the fact that conda is a cross-platform, language-agnostic package manager. It does not have to be for Python software; many of these packages are essentially just downloads that get expanded, but the process is more convenient and more secure than manually downloading and extracting archives.</p>
</div>
</section>
<section id="example-how-to-distribute-a-complex-scientific-software-environment-on-a-heterogeneous-hpc-cluster" class="level2">
<h2 class="anchored" data-anchor-id="example-how-to-distribute-a-complex-scientific-software-environment-on-a-heterogeneous-hpc-cluster">Example: how to distribute a complex scientific software environment on a heterogeneous HPC cluster</h2>
<p>SO:UK Data Centre example:</p>
<ul>
<li><a href="https://github.com/ickc/python-pmpm">ickc/python-pmpm: Python manual package manager</a>: built on top of conda to bootstrap Python packages and build tools (e.g.&nbsp;compilers)
<ul>
<li>optimize per microarch (<code>x86_64-v3</code>, <code>x86_64-v4</code>, etc.)</li>
</ul></li>
<li>CI/CD workflow using GitHub Actions to create a distribution with a predefined prefix: <a href="https://github.com/ickc/so-software-environment">ickc/so-software-environment: scripts and documentation on bootstrapping a SO software environment in SO:UK.</a></li>
<li>staged it via special node to unarchive it at that prefix, deploying the environment via CVMFS</li>
</ul>
<div class="notes">
<p>This real-world example from the SO:UK Data Centre demonstrates how to distribute a complex scientific software environment on a heterogeneous HPC cluster. The approach builds on conda to bootstrap Python packages and build tools, with optimization for different microarchitectures. A CI/CD workflow using GitHub Actions creates distributions with predefined prefixes, which are then deployed via CVMFS. This addresses the challenge of maintaining consistent environments across diverse computing resources.</p>
</div>
</section>
</section>
<section id="pixi" class="level1">
<h1>Pixi</h1>
<section id="introduction-1" class="level2">
<h2 class="anchored" data-anchor-id="introduction-1">Introduction</h2>
<ul>
<li>Modern drop-in replacement for Conda.</li>
<li>Project-centric (local environments), not global.</li>
<li>Automatic, cross-platform lock files for reproducibility.</li>
<li>Seamlessly integrates Conda and PyPI packages.</li>
<li>Built-in task runner (like <code>make</code>).</li>
<li>Single, faster CLI (combines <code>conda</code>, <code>pip</code>, <code>conda-lock</code>, etc.), inspired by modern package managers like Cargo and npm.</li>
<li>Global tool installation: <code>pixi global install</code> (similar to <code>pipx</code>)</li>
</ul>
</section>
<section id="example-pyautolens" class="level2">
<h2 class="anchored" data-anchor-id="example-pyautolens">Example: PyAutoLens</h2>
<p>From <a href="https://github.com/ickc/python-autojax/blob/c8a71287dd42752e95e06d3339eb44bc472c5d99/pixi.toml">python-autojax/pixi.toml at c8a71287dd42752e95e06d3339eb44bc472c5d99 · ickc/python-autojax</a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode toml code-with-copy"><code class="sourceCode toml"><span id="cb9-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[project]</span></span>
<span id="cb9-2"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">authors</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Kolen Cheung &lt;christian.kolen@gmail.com&gt;"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb9-3"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">channels</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"conda-forge"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb9-4"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"DiRAC: revealing the nature of dark matter with the James Webb space telescope and JAX"</span></span>
<span id="cb9-5"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">name</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"autojax"</span></span>
<span id="cb9-6"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">platforms</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"osx-arm64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux-64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux-aarch64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb9-7"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">version</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0.1.0"</span></span>
<span id="cb9-8"></span>
<span id="cb9-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[tasks]</span></span>
<span id="cb9-10"></span>
<span id="cb9-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[dependencies]</span></span>
<span id="cb9-12"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">python</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=3.9"</span></span>
<span id="cb9-13"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">numpy</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-14"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">numba</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-15"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">jax</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># build</span></span>
<span id="cb9-17"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">poetry</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># extras</span></span>
<span id="cb9-19"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">bump-my-version</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># tests</span></span>
<span id="cb9-21"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">coverage</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-22"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">pytest</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-23"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">pytest-benchmark</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># docs</span></span>
<span id="cb9-25"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">furo</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-26"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">linkify-it-py</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-27"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">myst-parser</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-28"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">sphinx</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-29"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">sphinx-autobuild</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-30"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">pygal</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=3.0.5,&lt;4"</span></span>
<span id="cb9-31"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">defopt</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=6.4.0,&lt;7"</span></span>
<span id="cb9-32"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">ipykernel</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=6.29.5,&lt;7"</span></span>
<span id="cb9-33"></span>
<span id="cb9-34"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[pypi-dependencies]</span></span>
<span id="cb9-35"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">sphinx-last-updated-by-git</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span></span>
<span id="cb9-36"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">sphinxcontrib-apidoc</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=0.5.0,&lt;1"</span></span>
<span id="cb9-37"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">autojax</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">path</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">editable</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb9-38"></span>
<span id="cb9-39"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[feature.cuda]</span></span>
<span id="cb9-40"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">system-requirements</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cuda</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"12"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb9-41"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">platforms</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux-64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux-aarch64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb9-42"></span>
<span id="cb9-43"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[feature.cuda.target.linux-64.dependencies]</span></span>
<span id="cb9-44"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">jaxlib</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">version</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">build</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"*cuda*"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb9-45"></span>
<span id="cb9-46"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[environments]</span></span>
<span id="cb9-47"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cuda</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cuda"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span></code></pre></div></div>
</section>
<section id="example-brownianspindynamics" class="level2">
<h2 class="anchored" data-anchor-id="example-brownianspindynamics">Example: BrownianSpinDynamics</h2>
<p>From <a href="https://github.com/Uni-of-Exeter/brownian-spin-dynamics/blob/b2ba42450fe0049c79eb27464eb2c8d1d16c87e6/pixi.toml">brownian-spin-dynamics/pixi.toml at b2ba42450fe0049c79eb27464eb2c8d1d16c87e6 · Uni-of-Exeter/brownian-spin-dynamics</a></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode toml code-with-copy"><code class="sourceCode toml"><span id="cb10-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[workspace]</span></span>
<span id="cb10-2"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">channels</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"conda-forge"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb10-3"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">platforms</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"win-64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux-64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux-aarch64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"osx-64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"osx-arm64"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb10-4"></span>
<span id="cb10-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[tasks]</span></span>
<span id="cb10-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># bootstrap</span></span>
<span id="cb10-7"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">bootstrap-julia</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"juliaup add $JULIAUP_CHANNEL"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"install julia version specified by JULIAUP_CHANNEL"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-8"></span>
<span id="cb10-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># resolve</span></span>
<span id="cb10-10"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">resolve</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">depends-on</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"resolve-root"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"resolve-library"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"resolve-docs"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"resolve environments"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-11"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">resolve-root</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=. -e 'using Pkg; Pkg.develop(PackageSpec(path=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">BrownianSpinDynamics</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)); Pkg.resolve()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"resolve root environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-12"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">resolve-library</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics -e 'using Pkg; Pkg.resolve()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"resolve library environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-13"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">resolve-docs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics/docs -e 'using Pkg; Pkg.develop(PackageSpec(path=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">BrownianSpinDynamics</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)); Pkg.resolve()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"resolve docs environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-14"></span>
<span id="cb10-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># update</span></span>
<span id="cb10-16"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">update</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">depends-on</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update-root"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update-library"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update-docs"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update environments"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-17"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">update-root</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=. -e 'using Pkg; Pkg.develop(PackageSpec(path=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">BrownianSpinDynamics</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)); Pkg.update()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update root environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-18"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">update-library</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics -e 'using Pkg; Pkg.update()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update library environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-19"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">update-docs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics/docs -e 'using Pkg; Pkg.develop(PackageSpec(path=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">BrownianSpinDynamics</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)); Pkg.update()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update docs environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-20"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">update-precompile</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics -e 'using Pkg; Pkg.precompile()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"update precompile environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-21"></span>
<span id="cb10-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># precompile</span></span>
<span id="cb10-23"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">precompile</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">depends-on</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"precompile-root"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"precompile-library"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"precompile-docs"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"precompile environments"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-24"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">precompile-root</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=. -e 'using Pkg; Pkg.develop(PackageSpec(path=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">BrownianSpinDynamics</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)); Pkg.instantiate(); Pkg.precompile()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"precompile root environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-25"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">precompile-library</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics -e 'using Pkg; Pkg.instantiate(); Pkg.precompile()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"precompile library environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-26"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">precompile-docs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics/docs -e 'using Pkg; Pkg.develop(PackageSpec(path=</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">BrownianSpinDynamics</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)); Pkg.instantiate(); Pkg.precompile()'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"precompile docs environment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-27"></span>
<span id="cb10-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># test</span></span>
<span id="cb10-29"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">test</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics integration_tests/runtests_all.jl"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"run all tests"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-30"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">test-unit</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics -e 'using Pkg; Pkg.test(test_args=ARGS, allow_reresolve = false)' {{ case }}"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">arg</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"case"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">default</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"run unit tests"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-31"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">test-integration</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics integration_tests/runtests.jl {{ case }}"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">arg</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"case"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">default</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"run integration tests"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-32"></span>
<span id="cb10-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># linting</span></span>
<span id="cb10-34"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">lint-aqua</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=. scripts/lint_package.jl"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lint the library with Aqua.jl"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb10-35"></span>
<span id="cb10-36"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># benchmarks</span></span>
<span id="cb10-37"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">bench</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=. BrownianSpinDynamics/bench/bench.jl {{ case }}"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">arg</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"case"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">default</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"run benchmarks"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-38"></span>
<span id="cb10-39"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># format</span></span>
<span id="cb10-40"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">format</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">depends-on</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pre-sync"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia-format"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"post-sync"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format everything"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-41"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">pre-sync</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"jupytext --sync 'tutorials/*.ipynb'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Synchronize ipynb,jl pairs using jupytext"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-42"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">post-sync</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"jupytext --sync 'tutorials/*.ipynb'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Synchronize ipynb,jl pairs using jupytext"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-43"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">julia-format</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia -e 'using JuliaFormatter; format(</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">.</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format all files using JuliaFormatter"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb10-44"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">format-library</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia -e 'using JuliaFormatter; format(</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">BrownianSpinDynamics</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\"</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">)'"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format BrownianSpinDynamics using JuliaFormatter"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-45"></span>
<span id="cb10-46"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># docs</span></span>
<span id="cb10-47"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">docs-build</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics/docs BrownianSpinDynamics/docs/make.jl"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"build docs"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-48"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">docs-serve</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=BrownianSpinDynamics/docs BrownianSpinDynamics/docs/serve.jl"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"serve docs"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-49"></span>
<span id="cb10-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># install</span></span>
<span id="cb10-51"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">install-kernel</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"julia --project=. scripts/install-julia-brownian-spin-dynamics.jl --overwrite"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"install Jupyter kernel for BrownianSpinDynamics"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-52"></span>
<span id="cb10-53"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># dev</span></span>
<span id="cb10-54"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">find-version</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{ </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">cmd</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"scripts/find-version.sh {{ pkg }}"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">, </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pkg"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">], </span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">description</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"find version of a package from Manifest.toml"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> }</span></span>
<span id="cb10-55"></span>
<span id="cb10-56"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[dependencies]</span></span>
<span id="cb10-57"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">juliaup</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=1.17.21,&lt;2"</span></span>
<span id="cb10-58"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">jupytext</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=1.17.2,&lt;2"</span></span>
<span id="cb10-59"></span>
<span id="cb10-60"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[activation.env]</span></span>
<span id="cb10-61"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">JULIA_PROJECT</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"@."</span></span>
<span id="cb10-62"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">JULIAUP_CHANNEL</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1.11.7"</span></span>
<span id="cb10-63"></span>
<span id="cb10-64"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># this put the .julia directory typically available in ~/.julia</span></span>
<span id="cb10-65"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># to the conda prefix that the pixi environment resides in</span></span>
<span id="cb10-66"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[target.unix.activation.env]</span></span>
<span id="cb10-67"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">JULIA_DEPOT_PATH</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$CONDA_PREFIX/.julia"</span></span>
<span id="cb10-68"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">JULIAUP_DEPOT_PATH</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$CONDA_PREFIX/.julia"</span></span>
<span id="cb10-69"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[target.win.activation.env]</span></span>
<span id="cb10-70"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">JULIA_DEPOT_PATH</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%CONDA_PREFIX%</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">.julia"</span></span>
<span id="cb10-71"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">JULIAUP_DEPOT_PATH</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%CONDA_PREFIX%</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">.julia"</span></span></code></pre></div></div>
</section>
</section>
<section id="nix" class="level1">
<h1>Nix</h1>
<section id="why-functional-package-manager" class="level2">
<h2 class="anchored" data-anchor-id="why-functional-package-manager">Why functional package manager?</h2>
<p>If we represent the lifecycle of reproducibility from source code and data to result via functions:</p>
<ol type="1">
<li><p><img src="https://latex.codecogs.com/png.latex?c_i%20=%20C(s_i,%20g_i(s_j))">: <strong>C</strong>ompilation takes <strong>s</strong>ource code and the dependency <strong>g</strong>raph to <strong>c</strong>ompiled binaries</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?e%20=%20G(c_i)">: <strong>e</strong>nvironment constructed from the whole dependency <strong>G</strong>raph of all pre<strong>c</strong>ompiled binaries</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?p_i%20=%20f_i(e,%20d_j)">: <strong>f</strong>ilters or <strong>f</strong>unctions that are an individual part of your scientific workflow, executing in the <strong>e</strong>nvironment and acting on your <strong>d</strong>ata to produce data <strong>p</strong>roducts.</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?r%20=%20W(e,%20f_i,%20d_j)">: a <strong>W</strong>orkflow that chains all these to obtain the final <strong>r</strong>esult.</p></li>
</ol>
<p>Then it becomes obvious that (3) is the job of the programmer, (4) is the job of the workflow manager to ensure that they are pure functions (so that it is reproducible given the same inputs.)</p>
<p>The remaining task (1) and (2) are the jobs of a package manager.</p>
<p>What if we can make them pure functions? That’s basically what a functional package manager does.</p>
</section>
<section id="impurity-in-building-software" class="level2">
<h2 class="anchored" data-anchor-id="impurity-in-building-software">Impurity in building software</h2>
<p>What could make it impure?</p>
<ul>
<li>OS:
<ul>
<li>change of OS</li>
<li>OS provided libraries or tooling</li>
</ul></li>
<li>hardware:
<ul>
<li>change of hardware (x86_64 vs aarch64, even microarch such as x86_64-v3 vs.&nbsp;v4, or accelerators)</li>
</ul></li>
<li>temporal decay:
<ul>
<li>link rot (e.g.&nbsp;the URL pointing to one of the dependencies is dead)</li>
</ul></li>
<li>non-reproducible build:
<ul>
<li>poor design of build script and system: timestamps, random ordering, etc.</li>
</ul></li>
</ul>
</section>
<section id="solutions-to-purity" class="level2">
<h2 class="anchored" data-anchor-id="solutions-to-purity">Solutions to purity</h2>
<ul>
<li>OS &amp; hardware:
<ul>
<li>do not change this, or</li>
<li>make sure everything is OS and hardware agnostic (including compiler, any source code (assembly?))</li>
<li>Linux kernel backward compatibility is a strong mitigation towards this problem.</li>
</ul></li>
<li>temporal decay:
<ul>
<li>link rot: Software Heritage archiving many softwares to prevent rotting</li>
<li>OS upgrade: this is an illusion of a temporal problem, see previous point</li>
</ul></li>
<li>reproducible build: there are many efforts to accomplish this, and it is beyond our scope as long as the result is functionally identical.</li>
</ul>
<p>On top of these, functional package manager guarantees building softwares is a pure function. Hence it is always reproducible.</p>
<p>(In contrast, despite all these efforts, non-functional package managers cannot guarantee purity, hence reproducibility.)</p>
<p>Nix (and also Guix, another functional package manager inspired by Nix) has various levels of integration with Software Heritage to automatically mitigate against link rot.</p>
</section>
</section>
<section id="spack" class="level1">
<h1>Spack</h1>
</section>
<section id="docker" class="level1">
<h1>Docker</h1>
</section>
<section id="misc." class="level1">
<h1>Misc.</h1>
<section id="reflections-on-trusting-trust" class="level2">
<h2 class="anchored" data-anchor-id="reflections-on-trusting-trust">Reflections on trusting trust</h2>
<p><span class="citation" data-cites="thompson_reflections_1984">Thompson (1984)</span></p>
<ul>
<li>Hermitic build
<ul>
<li>XZ backdoor incidence: non-reproducible build process</li>
<li>conda is not completely hermitic, but is close</li>
</ul></li>
<li>Hermitic build is a step towards reproducible build
<ul>
<li>conda-forge packages has a certain level of guarantee of reproducibility, PyPI’s do not</li>
</ul></li>
<li>Layers of maintainers
<ul>
<li>trusting the developer(s) directly. E.g.
<ul>
<li>Installing directly from GitHub Release</li>
<li>Installing from PyPI</li>
</ul></li>
<li>Installing from a package manager is trusting the package maintainer(s), the maintainer(s) of the index (, and the developer(s)). E.g.
<ul>
<li>Installing a conda-forge package maintained by me (package maintainer) with the governance of conda-forge (the people, infrastrcture code, infrastructure provider, etc.)</li>
</ul></li>
</ul></li>
</ul>
</section>
<section id="prefix-rpath-and-all-that" class="level2">
<h2 class="anchored" data-anchor-id="prefix-rpath-and-all-that">Prefix, RPATH, and all that</h2>
</section>
<section id="references" class="level2">




</section>
</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-hernandez_repeatability_2023" class="csl-entry">
Hernández, José Armando, and Miguel Colom. 2023. <span>“Repeatability, Reproducibility, Replicability, Reusability (4R) in Journals’ Policies and Software/Data Management in Scientific Publications: A Survey, Discussion, and Perspectives.”</span> arXiv:2312.11028. Preprint, arXiv, December 18. <a href="https://doi.org/10.48550/arXiv.2312.11028">https://doi.org/10.48550/arXiv.2312.11028</a>.
</div>
<div id="ref-plesser_reproducibility_2018" class="csl-entry">
Plesser, Hans E. 2018. <span>“Reproducibility Vs. Replicability: A Brief History of a Confused Terminology.”</span> <em>Frontiers in Neuroinformatics</em> 11 (January): 76. <a href="https://doi.org/10.3389/fninf.2017.00076">https://doi.org/10.3389/fninf.2017.00076</a>.
</div>
<div id="ref-thompson_reflections_1984" class="csl-entry">
Thompson, Ken. 1984. <span>“Reflections on Trusting Trust.”</span> <em>Communications of the ACM</em> 27 (8): 761–63. <a href="https://doi.org/10.1145/358198.358210">https://doi.org/10.1145/358198.358210</a>.
</div>
</div></section></div> ]]></description>
  <category>Internal presentation</category>
  <category>Reproducibility</category>
  <guid>https://blog.kolen.dev/RSE/UoE/2025-11-26-reproducibility-article.html</guid>
  <pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>JIT compilers for scientific computing in Python: Numba vs. JAX</title>
  <dc:creator>Kolen Cheung</dc:creator>
  <link>https://blog.kolen.dev/RSE/PyAutoLens/2025-09-21-autojax-article.html</link>
  <description><![CDATA[ 





<!-- Everything before the first heading is article-only. Keep this comment
INSIDE the div: a raw HTML block out here survives into slidy and pandoc turns
it into a stray empty first slide. -->
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><embed src="./2025-09-21-autojax.html" style="width:100.0%" height="400"></p>
<figcaption><a href="./2025-09-21-autojax.html">Slide to the talk</a></figcaption>
</figure>
</div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/4gVWeLfMdgc?si=cLcQ63xMOjYofdgH" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="">
</iframe>
<p>Below is a companion article to the PyCon UK 2025 talk on porting a gravitational lensing likelihood from Numba to JAX for JWST analysis. The slides and this article are generated from a single source, so everything that was on screen is here, together with what was said around it.</p>
<p>Today I’m going to talk about JIT compilers for scientific computing in Python — Numba versus JAX — through a case study on evaluating a gravitational lensing likelihood.</p>
<section id="context-why-python-in-hpc" class="level1">
<h1>Context: Why Python in HPC?</h1>
<section id="why-do-i-use-python-in-hpc" class="level2">
<h2 class="anchored" data-anchor-id="why-do-i-use-python-in-hpc">Why do I use Python in HPC?</h2>
<ul>
<li>Cosmologist</li>
<li>PhD: CMB Data Analysis
<ul>
<li>processing <img src="https://latex.codecogs.com/png.latex?%5Csim%20O(10%5Ctext%7B%20TiB%7D)%20%5Csim%20O(%5Ctext%7BPiB%7D)"> of data for cosmological inference</li>
<li>using scientific softwares in Python</li>
<li>running on NERSC (a top 10 HPC system)</li>
</ul></li>
</ul>
<div class="notes">
<p>Let me start with why <em>I</em> use it.</p>
<p>I am a cosmologist. I did my PhD in the UC Berkeley Physics department on the topic of the Cosmic Microwave Background (CMB) radiation — the relic radiation that is everywhere, so that every time you look at the sky you are looking at it, you just don’t know it. During my research I used the NERSC HPC facility in the US, a top 10 supercomputing system, to perform CMB data analysis. HPC stands for High Performance Computing, also known as supercomputing.</p>
<p>Briefly speaking, as we cannot do an experiment cosmologically, we instead observe a huge amount of data and use statistical methods such as Bayesian inference to deduce information about our universe, such as deducing the dark matter composition, or finding evidence of inflation during the Big Bang. My main interest in Python is therefore primarily its application to scientific computing on HPC.</p>
</div>
<!-- ## Short introduction on parallel computers

- SIMD: a single core performing multiple floating operations at once
- Multi-threading: multiple CPU cores sharing the same pool of memory working together in concert
- Message-passing: explicit data transfer between processes, often across different nodes (computers)

::: notes

What is an HPC though? HPC stands for High Performance Computing.
Nowadays they are massively parallel computers.
A traditional HPC consists of thousands of nodes, where each node is a "computer" consisting of dozens to hundreds of CPU cores, where as each core can perform multiple floating point operations at once.
This naturally falls into a hierarchy of SIMD + multi-threading + message passing.

Roughly speaking, message passing is responsible to efficiently use all available nodes altogether, and can be done by using MPI libraries such as `mpi4py` in Python.

For any language to suceed in the HPC space, it must be able to handle the first 2 cases efficiently.

To make the matter more complicated, nowadays HPC are not "traditional" anymore, where accelerators such as GPU are attached on each node. We will come back to this point more later.

::: -->
</section>
<section id="questions-which-language-can-you-use-to-write-applications-for-hpc" class="level2">
<h2 class="anchored" data-anchor-id="questions-which-language-can-you-use-to-write-applications-for-hpc">Questions: which language can you use to write applications for HPC?</h2>
<p>Languages that has demonstrated to scale to state-of-the-art, full system supercomputer</p>
<div class="incremental">
<ul class="incremental">
<li>Fortran</li>
<li>C/C++</li>
<li>Python</li>
<li>Julia</li>
<li><del>matlab</del></li>
<li><del>rust</del></li>
</ul>
</div>
<div class="notes">
<p>This was a question to the room, so treat it as one here too. By “can you use”, I mean a language that has demonstrated it scales to a state-of-the-art, full-system supercomputer — not merely a language you can invoke on one, the way you would invoke bash.</p>
<p>The room got Fortran and C/C++ immediately. The next answer is Python, and you might call that cheating a little bit, because many of the codes you call from Python to run on an HPC system were written in Fortran or C and are simply exposed as a module. It is still very useful as the glue that holds everything together. Julia belongs on the list as well. MATLAB and Rust do not — not because they are bad languages, but because they have not yet demonstrated that they scale to a full system.</p>
</div>
</section>
<section id="questions-why-python-in-hpc-what-is-its-superpower-in-supercomputing" class="level2">
<h2 class="anchored" data-anchor-id="questions-why-python-in-hpc-what-is-its-superpower-in-supercomputing">Questions: Why Python in HPC? What is its superpower in supercomputing?</h2>
<div class="incremental">
<ul class="incremental">
<li>Because it is where the community is.</li>
<li>Because we are the people who don’t care what language our algorithm is implemented in.
<ul class="incremental">
<li>C</li>
<li>C++</li>
<li>Fortran</li>
<li>Julia</li>
<li>Rust</li>
<li>…</li>
</ul></li>
</ul>
</div>
<div class="notes">
<p>The opening keynote resonates with me a lot. Python has superpowers. I recommend people watching that to see the history and the bigger picture. Here, I am more narrowly focused to tell you why, at this particular moment, Python is the language of choice to write and run scientific applications on supercomputers.</p>
<p>The room offered embarrassingly parallel workloads, and developer efficiency. Both are true — and even when the problem is <em>not</em> embarrassingly parallel you can still drive it from Python. But my own answer is this.</p>
<p>First of all, it is where the community is. NumPy, SciPy, Astropy, you name it. I am not claiming to explain how it got here, or whether it will still be the case in the future; only that at this moment, this is where the community is.</p>
<p>Secondly — and I don’t know whether this is a blasphemy — it is because we are the people who don’t care what language our algorithm is implemented in. CPython itself is written in C. C++ is exposed as a Python module through pybind11. Many SciPy functions are Fortran underneath. Write something in Julia, or in Rust, and you can still expose it with a Python interface.</p>
<p>In other words, Python is the glue. If we look back to the list of languages that has demonstrate they can scale to the state-of-the-art supercomputer at a given era, there is not a lot on the list, and Python is one of them. You might call that cheating, because in this case Python is mostly just calling C modules or libraries written in Fortran. But we don’t care. Python is the glue that successfully composed very complicated scientific workflow together.</p>
</div>
</section>
</section>
<section id="context-why-jit" class="level1">
<h1>Context: Why JIT?</h1>
<section id="short-introduction-on-aotjit-compilations-interpreter" class="level2">
<h2 class="anchored" data-anchor-id="short-introduction-on-aotjit-compilations-interpreter">Short introduction on aot/jit compilations &amp; interpreter</h2>
<ul>
<li>interpreters
<ul>
<li>CPython</li>
<li>bash</li>
</ul></li>
<li>compilers
<ul>
<li>AOT
<ul>
<li><code>gcc</code> from GNU compilers, <code>clang</code> from LLVM compilers
<ul>
<li>CPython is AOT compiled by these compilers!</li>
</ul></li>
<li>traditionally excels at HPC: C/C++, Fortran</li>
</ul></li>
<li>JIT
<ul>
<li><code>pypy</code></li>
<li>Julia</li>
<li>Javascript has a JIT</li>
</ul></li>
</ul></li>
</ul>
<div class="notes">
<p>So, why JIT? To answer that I need to give two short introductions first.</p>
<p>Back to the languages, focusing on the aspect of how source code eventually runs, it falls into two categories, compilers and interpreters. And within compilers, there are also ahead-of-time (AOT) compilation and just-in-time (JIT) compilation.</p>
<p>Here we will name a few examples. The reference Python implementation, <code>CPython</code>, is an interpreter, which is AOT compiled by C compilers such as <code>gcc</code> and <code>clang</code>. Bash is another commonly used interpreted language, and I would say that in the HPC space, Python is increasingly taking over what bash used to do. AOT compilation is what traditionally excels at HPC: C, C++, Fortran.</p>
<p>Another well known Python implementation <code>pypy</code> is a jit compiler — supposedly any valid Python program can run under it. Julia and JavaScript have JITs too.</p>
</div>
</section>
<section id="short-introduction-on-the-landscape-of-acceleration-framework-of-numeric-code-in-python" class="level2">
<h2 class="anchored" data-anchor-id="short-introduction-on-the-landscape-of-acceleration-framework-of-numeric-code-in-python">Short introduction on the landscape of acceleration framework of numeric code in Python</h2>
<ul>
<li>AOT
<ul>
<li>C with CPython API
<ul>
<li>e.g.&nbsp;Numpy, which is a framework in itself</li>
</ul></li>
<li>Cython: superset of the Python language, compiled to C modules, handle CPython API and Python interface automatically</li>
<li>pybind11: C++ 11+, handle CPython API and Python interface semi-automatically</li>
</ul></li>
<li>JIT
<ul>
<li><code>pypy</code>: general purpose Python implementation (any valid Python should runs)</li>
<li>CPython 3.13+ experimental JIT: only a subset of Python code will be jit compiled (and is not clear on when and where), not focused on numerical</li>
<li>Numba, JAX: DSL, jit-compile a subset of Python, focused on numerical</li>
</ul></li>
</ul>
<div class="notes">
<p>The second introduction is how you accelerate a piece of Python code when you need to.</p>
<p>I hope we all agree the CPython interpreter is slow, right? To accelerate applications running in CPython, there are multiple frameworks to do it. The traditional model is C modules: write your performance critical part of the code in C and use the CPython API to expose a Python interface to the users. A prominent example of that is <code>numpy</code>, which defines an interface often used in high performance numerical operations, and is architected in C with a pythontic interface. This is still one of the fastest and pythonic way to write numerical code in Python without introducing further compliation to the users. Other examples on AOT are cython and C++ with pybind11, which I won’t go into details.</p>
<p>Turning our attention to the spotlight of the day: JIT. <code>pypy</code> is a general purpose Python jit compiled runtime for a long time. There is also an experimental JIT compiler in CPython 3.13+, which would jit compiled a subset of hot code: anything valid still runs, but only some of it is actually compiled, and it is not aimed at numerics.</p>
<p>The remaining JIT compilers are the one we are focusing on today. Numba and JAX can both be regarded as Domain Specfic Languages that will jit compiled a subset of the Python language, focused on numerical compuations.</p>
</div>
</section>
<section id="why-jit-solving-the-2-language-problem" class="level2">
<h2 class="anchored" data-anchor-id="why-jit-solving-the-2-language-problem">Why jit: solving the 2 language problem</h2>
<div class="columns">
<div class="column" style="width:50%;">
<p>To replace this single function from Numba to C++ with pybind11,</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@numba.jit</span>(parallel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb1-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> _fma(out, weights, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span>arrays):</span>
<span id="cb1-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> weight, array <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">zip</span>(weights, arrays):</span>
<span id="cb1-4">        out <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> weight <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> array</span></code></pre></div></div>
<p><a href="https://github.com/hpc4cmb/toast/commit/a38d1d6dbcc97001a1ad1c315bb08cf1eecc74c7"><code>hpc4cmb/toast#a38d1d6</code></a>:</p>
<pre class="log"><code>14 files changed
+230 -36 lines changed</code></pre>
<p>… and 30% faster!</p>
</div><div class="column" style="width:50%;">
<ul>
<li><p>Python gives you velocity: rapid prototyping science code is a path dependent evolution</p></li>
<li><p>Numba jit gives you speed (SIMD + multi-threading): C++ with SIMD and OpenMP multi-threading is only 30% faster in this case. The single <code>@jit</code> decorator gives you 3 times speed up comparing to pure Numpy implementation.</p></li>
<li><p>JIT sometimes has advantage over AOT because it can see the data</p></li>
<li><p>JIT obviously has overhead, but if you are processing “big data”, that amount of time usually is much shorter than it takes to run the calculation itself.</p></li>
</ul>
</div>
</div>
<div class="notes">
<p>My headline answer to “why JIT” is that it solves the two-language problem: you no longer need two different languages to implement very high performance code.</p>
<p>To briefly give an idea why JIT may be useful, we can look at this function as an example. If you are familiar with Numba enough, as soon as you write down this simple function with Numpy, adding the jit decorator there is a no-brainer. You would be able to immediately predict that it will be faster — three times faster than the pure NumPy version, in this case — and that memory allocation is simpler, and hence lesser memory pressure and lesser chance of getting killed by out-of-memory (OOM) error.</p>
<p>However, when I ported this to C++ with pybind11, 14 files are changed, 230 lines are added. If you look into the commit change, for sure a lot of them are boilerplates. But that is exactly the point. It went from using Numba as a 10s decision to being 30% faster after a ton of work. One single line gave three times; all of that work gave 30%.</p>
<p>This sort of things happens all the time when writing science code. How your program eventually got there is often not obvious in the beginning. It involves repid prototyping and sometimes solving complicated mathematical or algorithmic problems. Having a DSL within Python that can be jitted is a superpower Python given us to move fast and run fast at the same time.</p>
</div>
</section>
</section>
<section id="concrete-example-of-numba-vs.-jax" class="level1">
<h1>Concrete example of Numba vs.&nbsp;JAX</h1>
<section id="tildew" class="level2">
<h2 class="anchored" data-anchor-id="tildew"><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D"></h2>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D_%7Bij%7D%20=%20%5Csum_%7Bk=1%7D%5EK%20%5Cfrac%7B1%7D%7Bn_k%5E2%7D%20%5Ccos%20%5Cleft(%202%20%5Cpi%20%5Cleft%5B%20%5Cleft(%5Cvec%7Bg%7D_i%20-%20%5Cvec%7Bg%7D_j%20%5Cright)%20%5Ccdot%20%5Cvec%7Bu%7D_k%20%5Cright%5D%20%5Cright)%20,%5Cquad%201%20%5Cleq%20i,%20j%20%5Cleq%20M"></p>
<div class="notes">
<p>Now it all makes sense, kind of. Let us do an actual example to see how JIT works.</p>
<p>I call this a slow track. It is a live demo without being live. I hope it will gives you a feel of the languages and gain intuitions yourself. Later on, we will have a fast track and you just have to believe the high level summaries I am going to give you.</p>
<p>Let’s stare at this equation and see how you would implement it.</p>
</div>
</section>
<section id="numpy" class="level2">
<h2 class="anchored" data-anchor-id="numpy">Numpy</h2>
<p>As usual in Numpy, we vectorize everything:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> np</span>
<span id="cb3-2"></span>
<span id="cb3-3"></span>
<span id="cb3-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_mm_np(</span>
<span id="cb3-5">    n_k: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb3-6">    u_k_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb3-7">    g_m_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb3-8">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb3-9">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (M, M, 1, 2)</span></span>
<span id="cb3-10">    δg_mm1_vec <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>  g_m_vec.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> g_m_vec.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb3-11">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 1, K, 2)</span></span>
<span id="cb3-12">    u_11k_vec <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> u_k_vec.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb3-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (</span>
<span id="cb3-14">        np.cos(</span>
<span id="cb3-15">            (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span></span>
<span id="cb3-16">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (M, M, K)</span></span>
<span id="cb3-17">            (</span>
<span id="cb3-18">                δg_mm1_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_11k_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-19">                δg_mm1_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_11k_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb3-20">            )</span>
<span id="cb3-21">        ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span></span>
<span id="cb3-22">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 1, K)</span></span>
<span id="cb3-23">        np.square(n_k).reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-24">    ).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sum over k</span></span></code></pre></div></div>
<pre class="log"><code>898 ms ± 9.62 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)</code></pre>
<div class="notes">
<p>The function is not really very long — it is almost a one-liner. I have just given it a lot of whitespace to make it clearer what is happening, but it is only implementing the math as it is written. And it is reasonably fast.</p>
<p>Ignore the tiny little details there where the order of the dimensions in <img src="https://latex.codecogs.com/png.latex?%5Cvec%7Bg%7D"> and <img src="https://latex.codecogs.com/png.latex?%5Cvec%7Bu%7D"> are opposite to each other.</p>
</div>
</section>
<section id="numba" class="level2">
<h2 class="anchored" data-anchor-id="numba">Numba</h2>
<p>How would you implement it in Numba? Just add the jit decorator:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> numba</span>
<span id="cb5-2"></span>
<span id="cb5-3"></span>
<span id="cb5-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@numba.jit</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f8[:, ::1](f8[::1], f8[:, ::1], f8[:, ::1])"</span>, nopython<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, nogil<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, parallel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb5-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_mm_numba(</span>
<span id="cb5-6">    ...</span></code></pre></div></div>
<pre class="log"><code>522 ms ± 9.01 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)</code></pre>
<div class="notes">
<p>Mine looks a little complicated, but the part that really makes the difference is <code>parallel=True</code>. It is a very simple decorator to add, and you can immediately see that it is faster.</p>
</div>
</section>
<section id="jax" class="level2">
<h2 class="anchored" data-anchor-id="jax">JAX</h2>
<p>How would you implement it in JAX? By add the jit decorator, and replacing <code>numpy</code> with <code>jax.numpy</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> jax</span>
<span id="cb7-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> jax.numpy <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> jnp</span>
<span id="cb7-3"></span>
<span id="cb7-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jax.jit</span></span>
<span id="cb7-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_mm_jax(</span>
<span id="cb7-6">    n_k: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb7-7">    u_k_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb7-8">    g_m_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb7-9">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb7-10">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (M, M, 1, 2)</span></span>
<span id="cb7-11">    δg_mm1_vec <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>  g_m_vec.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> g_m_vec.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb7-12">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 1, K, 2)</span></span>
<span id="cb7-13">    u_11k_vec <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> u_k_vec.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb7-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (</span>
<span id="cb7-15">        jnp.cos(</span>
<span id="cb7-16">            (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> jnp.pi) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span></span>
<span id="cb7-17">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (M, M, K)</span></span>
<span id="cb7-18">            (</span>
<span id="cb7-19">                δg_mm1_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_11k_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb7-20">                δg_mm1_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_11k_vec[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb7-21">            )</span>
<span id="cb7-22">        ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span></span>
<span id="cb7-23">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 1, K)</span></span>
<span id="cb7-24">        jnp.square(n_k).reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb7-25">    ).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sum over k</span></span></code></pre></div></div>
<pre class="log"><code>144 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)</code></pre>
<div class="notes">
<p>Can you guess? Just change the decorator — plus one more thing. Plain NumPy does not work inside a JAX-jitted function, but JAX ships its own module, <code>jax.numpy</code>, which is almost a drop-in replacement: wherever you had <code>np</code>, you use <code>jnp</code>, conventionally imported under that name, and it works.</p>
<p>You would notice the speed up from Numpy to Numba to JAX, in this order, which is often the case. There is a general trend of progress here: you start with something in NumPy, you implement it in Numba and it is faster, and then in JAX — where it is not even a reimplementation, just a change in how it is compiled — and it is faster still. That is the general trend, though I am not saying this is always the case.</p>
<p>Ok, case closed, right?</p>
<p>Hold on, not so fast. Does anyone spot any potential problem with this implementation?</p>
</div>
</section>
<section id="case-closed" class="level2">
<h2 class="anchored" data-anchor-id="case-closed">Case closed?</h2>
<div class="notes">
<p>All three implementations are the same in this respect, so here is a hint: go back to the NumPy listing and look at the comment marking <code>(M, M, K)</code>. What it means is that an array of that shape is materialized at that point in the expression.</p>
</div>
</section>
<section id="digression-in-problem-sizes" class="level2">
<h2 class="anchored" data-anchor-id="digression-in-problem-sizes">Digression in problem sizes</h2>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D_%7Bij%7D%20=%20%5Csum_%7Bk=1%7D%5EK%20%5Cfrac%7B1%7D%7Bn_k%5E2%7D%20%5Ccos%20%5Cleft(%202%20%5Cpi%20%5Cleft%5B%20%5Cleft(%5Cvec%7Bg%7D_i%20-%20%5Cvec%7Bg%7D_j%20%5Cright)%20%5Ccdot%20%5Cvec%7Bu%7D_k%20%5Cright%5D%20%5Cright)%20,%5Cquad%201%20%5Cleq%20i,%20j%20%5Cleq%20M"></p>
<dl>
<dt>Number of image pixels</dt>
<dd>
<img src="https://latex.codecogs.com/png.latex?M%20%5Csim%2070,000%20%5CRightarrow%20M%5E2%20%5Csim%205%20%5Ctimes%2010%5E9,%20%5Cquad%200%20%5Cleq%20i,%20j%20%3C%20M">
</dd>
<dt>Number of visibilities</dt>
<dd>
<img src="https://latex.codecogs.com/png.latex?K%20%5Csim%2010%5E7,%20%5Cquad%200%20%5Cleq%20k%20%3C%20K">
</dd>
</dl>
<p><img src="https://latex.codecogs.com/png.latex?(M,%20M,%20K,%202)"> of 64-bit array would be <img src="https://latex.codecogs.com/png.latex?%5Csim%20700"> PiB!</p>
<p>While <img src="https://latex.codecogs.com/png.latex?(M,%20M)"> of 64-bit array would be <img src="https://latex.codecogs.com/png.latex?%5Csim%2040"> GiB only.</p>
<p>To put that into perspective, whole system aggregated memory of NERSC is <img src="https://latex.codecogs.com/png.latex?%5Csim%202%5Ctext%7B%20PiB%7D">.</p>
<div class="notes">
<p>I haven’t told you yet how large the problem is. <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D_%7Bij%7D"> is an <img src="https://latex.codecogs.com/png.latex?M%20%5Ctimes%20M"> matrix, where <img src="https://latex.codecogs.com/png.latex?M"> is the number of image pixels and can be as large as 70,000. <img src="https://latex.codecogs.com/png.latex?K"> is the number of so-called visibilities, and can be as large as <img src="https://latex.codecogs.com/png.latex?10%5E7">. So if you work out the size of the array being created at that intermediate step, it is on the order of hundreds of PiB. There is simply no computer that will fit that in memory.</p>
<p>The answer itself is fine — an <img src="https://latex.codecogs.com/png.latex?(M,%20M)"> array of 64-bit floats is about 40 GiB. The problem is not the answer, it is the intermediate.</p>
</div>
</section>
<section id="numpylow-memory-version" class="level2">
<h2 class="anchored" data-anchor-id="numpylow-memory-version">Numpy—low memory version</h2>
<ul>
<li>avoid expanding <img src="https://latex.codecogs.com/png.latex?K"></li>
</ul>
<div class="notes">
<p>The solution is simple, we do not want to expand the <img src="https://latex.codecogs.com/png.latex?K">-dimension fully in memory. We want to evaluate that lazily and accumulate it over <img src="https://latex.codecogs.com/png.latex?k">.</p>
<p>How would you do that in NumPy? You can always write a Python loop over <img src="https://latex.codecogs.com/png.latex?k"> — but would that be efficient? Probably not. There is no efficient solution in numpy however without using Python loops. I think there isn’t one; I can’t prove it. Let me know if you know a way.</p>
</div>
</section>
<section id="numbalow-memory-version" class="level2">
<h2 class="anchored" data-anchor-id="numbalow-memory-version">Numba—low memory version</h2>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb9-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@numba.jit</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f8[:, ::1](f8[::1], f8[:, ::1], f8[:, ::1])"</span>, nopython<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, nogil<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, parallel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb9-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_mm_numba_iterative(</span>
<span id="cb9-3">    n_k: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb9-4">    u_k_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb9-5">    g_m_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb9-6">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb9-7">    M <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> g_m_vec.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb9-8">    K <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> u_k_vec.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb9-9">    δg_mm_vec <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> g_m_vec.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> g_m_vec.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb9-10"></span>
<span id="cb9-11">    w_mm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((M, M))</span>
<span id="cb9-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> k <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> numba.prange(K):</span>
<span id="cb9-13">        w_mm <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.cos((<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (δg_mm_vec[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_k_vec[k, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> δg_mm_vec[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_k_vec[k, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> np.square(n_k[k])</span>
<span id="cb9-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> w_mm</span></code></pre></div></div>
<pre class="log"><code>55 ms ± 1.59 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)</code></pre>
<div class="notes">
<p>In Numba, however, we can have efficient loops, we can even do it in parallel. This function looks very similar to the version before: that one line is computing the same equation for a fixed value of <img src="https://latex.codecogs.com/png.latex?k">, you loop over <img src="https://latex.codecogs.com/png.latex?k">, and you do an in-place reduced sum. That is all. It will handles the reduced sum there in parallel correctly for you. It actually maps very nicely to C with SIMD and OpenMP parallel-for there. In fact, OpenMP is one of the threading layer backend in Numba.</p>
<p>A large part of why it is this fast is <code>numba.prange</code>, which parallelizes the loop.</p>
</div>
</section>
<section id="jaxlow-memory-version" class="level2">
<h2 class="anchored" data-anchor-id="jaxlow-memory-version">JAX—low memory version</h2>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jax.jit</span></span>
<span id="cb11-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_mm_jax_iterative(</span>
<span id="cb11-3">    n_k: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb11-4">    u_k_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb11-5">    g_m_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb11-6">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb11-7">    M <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> g_m_vec.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb11-8">    δg_mm_vec <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> g_m_vec.reshape(M, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> g_m_vec.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, M, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb11-9">    δg_mm_y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> δg_mm_vec[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb11-10">    δg_mm_x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> δg_mm_vec[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb11-11"></span>
<span id="cb11-12">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> _w_mm_k(</span>
<span id="cb11-13">        n: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>,</span>
<span id="cb11-14">        u_vec: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb11-15">    ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb11-16">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> jnp.cos((<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> jnp.pi) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (δg_mm_x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_vec[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> δg_mm_y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_vec[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> (n <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> n)</span>
<span id="cb11-17"></span>
<span id="cb11-18">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> _accumulate_w_mm(</span>
<span id="cb11-19">        sum_: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb11-20">        args: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]],</span>
<span id="cb11-21">    ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64], <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>]:</span>
<span id="cb11-22">        n, u_vec <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> args</span>
<span id="cb11-23">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> sum_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> _w_mm_k(n, u_vec), <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb11-24"></span>
<span id="cb11-25">    res, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> jax.lax.scan(</span>
<span id="cb11-26">        _accumulate_w_mm,</span>
<span id="cb11-27">        jnp.zeros((M, M)),</span>
<span id="cb11-28">        (</span>
<span id="cb11-29">            n_k,</span>
<span id="cb11-30">            u_k_vec,</span>
<span id="cb11-31">        ),</span>
<span id="cb11-32">    )</span>
<span id="cb11-33">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> res</span></code></pre></div></div>
<pre class="log"><code>86.6 ms ± 102 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)</code></pre>
<div class="notes">
<p>This means we can does the same in JAX, right? Wrong. It is very tempting to just swap the decorator — that is the whole promise of a decorator, that you put it there and it works — but here it won’t.</p>
<p>This is where one of the core design of JAX matters. JAX is essentially designed with pure functional programming paradigm in mind. It enforces immutable data, where the in-place reduced sum there is prohibited. JAX dislikes anything that mutates your state.</p>
<p>It does not mean it does not have semantics for looping. To express exactly the same logic, you refactor it in a certain way: a kernel function evaluating the same equation for one particular value of <img src="https://latex.codecogs.com/png.latex?k">, and then a way to tell the program to iterate over all <img src="https://latex.codecogs.com/png.latex?k"> and sum them up. Here, I choose <code>scan</code>, one of the idiomatic JAX interfaces for that, and itself a functional programming construct. What it does basically is to focus on each term for a dixed <img src="https://latex.codecogs.com/png.latex?k">, and then accumulate the sum over different values of <img src="https://latex.codecogs.com/png.latex?k">. If you believe me, this represents almost exactly the same thing as the Numba version, in the JAX-idiomatic way.</p>
<p>See <a href="https://github.com/ickc/python-autojax/tree/main/experiments/reduced_sum">python-autojax/experiments/reduced_sum at main · ickc/python-autojax</a> for more details on how this can be expressed in JAX.</p>
</div>
</section>
</section>
<section id="numba-vs.-jax-on-paper" class="level1">
<h1>Numba vs.&nbsp;JAX on paper</h1>
<section id="what-is-numba-jax" class="level2">
<h2 class="anchored" data-anchor-id="what-is-numba-jax">What is Numba / JAX</h2>
<ul>
<li>Numba
<ul>
<li>jit compiler powered by LLVM compiler</li>
<li>CPU only
<ul>
<li><code>numba.cuda</code> is an entirely different interface</li>
</ul></li>
</ul></li>
<li>JAX
<ul>
<li>tracing, jit compiler powered by XLA compiler</li>
<li>designed for machine learning workflow</li>
<li>no side effects <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> functional paradiagm</li>
<li>targets CPU, GPU (NVidia, AMD, Intel, Apple), TPU simultaneously</li>
<li>solving 2 language problem</li>
<li>solving “3 implementations problem”</li>
</ul></li>
<li>Better think of them as language + compiler + library</li>
</ul>
<div class="notes">
<p>This section is the fast track, and it is a bit denser than the rest. On the day I was short on time and skipped some of it; the article does not have to.</p>
<p>Now that we have seen some examples on how jit works, we will introduce them once more in details.</p>
<p>Numba is a jit compiler supporting a subset of Python and NumPy operations, powered by LLVM. While it is possible to target the GPU via CUDA, it requires rewriting the function using different APIs and paradigms, not to mention it is CUDA (i.e.&nbsp;NVidia) only.</p>
<p>JAX is a jit compiler, tracing compiler by Google, powered by XLA compiler, originated from Google. XLA stands for Accelerated Linear Algebra, which already tells you where its focus lies. JAX is designed primarily for machine learning workloads but is suitable for scientific computing as well, because the two have a lot in common. It is a tracing compiler removing side-effects of function. I.e. effectively it encourages functional programming paradigm and thinking. It automatically targets multiple hardware architectures including CPU, GPU, TPU, without requiring rewriting. I.e. it solves the “two-language problem”, or more accurately, “three-implementation problem”: prototype/API, CPU, GPU.</p>
<p>This is where JAX shines over other solutions to develop for the GPU. In principle, one single implementation is all you need. You do not need to optimize a function for the CPU, optimize another for the GPU.</p>
<p>One more tip for thinking about either of them: it is better to regard the thing named “Numba” or “JAX” as a language, a compiler, and a library all at once. A domain-specific language, because not everything can be jit-compiled; a compiler that does the compiling; and a set of libraries that come along with it. The comparison below is organized along those three lines.</p>
</div>
</section>
<section id="numba-vs.-jax" class="level2">
<h2 class="anchored" data-anchor-id="numba-vs.-jax">Numba vs.&nbsp;JAX</h2>
<table class="caption-top table">
<caption>Numba vs.&nbsp;JAX</caption>
<thead>
<tr class="header">
<th>Numba</th>
<th>JAX</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>C-like mini language</td>
<td>Smaller language (<img src="https://latex.codecogs.com/png.latex?%5Ctext%7BJAX%7D%20%5Cunderset%7B%5Csim%7D%7B%5Csubset%7D%20%5Ctext%7BNumba%7D">): restrictions on control flow, mutation, and dynamic shapes</td>
</tr>
<tr class="even">
<td>Implements a subset of Python+NumPy, with a parallelization model similar to a mini-“OpenMP”</td>
<td>Implements a subset of Python+NumPy+SciPy exposed via duck-typing.</td>
</tr>
<tr class="odd">
<td>NumPy implementations are dropped in replacement but only a subset is implemented. Calling NumPy within jitted function is completely hijacked. <a href="https://numba.readthedocs.io/en/stable/reference/numpysupported.html">Documentation is minimal.</a></td>
<td><a href="https://docs.jax.dev/en/latest/jax.numpy.html"><code>jax.numpy</code></a> and <a href="https://docs.jax.dev/en/latest/jax.scipy.html"><code>jax.scipy</code></a> have similar API comparing to NumPy and SciPy, but has its own documentation. This facilitates <a href="https://docs.jax.dev/en/latest/notebooks/Common_Gotchas_in_JAX.html">deviations in behaviors</a>.</td>
</tr>
<tr class="even">
<td>Functions “recompile” whenever input type changes.</td>
<td>Functions “recompile” whenever input type <strong>and shape</strong> changes.</td>
</tr>
<tr class="odd">
<td>No automatic compiling &amp; offloading to accelerator. No autograd/autodiff.</td>
<td>Going through FFI is more costly: memory transfer from and to device, losing autograd/autodiff.</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>As a language, Numba is more like a C-like mini language: mostly anything you can express in C can be expressed in Numba. JAX is a smaller language still — more restrictions on control flow, on mutation, and on dynamic shapes; you basically cannot do any of those.</p>
<p>As a library, Numba implements a subset of Python and NumPy with a parallelization model of its own. NumPy in Numba is a drop-in replacement, but only a subset of those operations is implemented, and whenever NumPy is called within a Numba jit-compiled function, it is completely hijacked: it is not really calling NumPy, Numba takes over and does its own thing. JAX, on the other hand, implements a subset of Python, NumPy <em>and</em> SciPy, so it is the larger library, and it lives in its own namespaces, <code>jax.numpy</code> and <code>jax.scipy</code>. The APIs are similar to NumPy and SciPy — most of them are the same — but they have their own documentation, and that documentation is precisely what makes it practical for JAX to deviate in behavior where its own restrictions, such as the functional paradigm we mentioned, require it.</p>
</div>
</section>
<section id="characteristics-of-jax" class="level2">
<h2 class="anchored" data-anchor-id="characteristics-of-jax">Characteristics of JAX</h2>
<ul>
<li><p>tracing compiler &amp; recompile per shape change <img src="https://latex.codecogs.com/png.latex?%5CRightarrow"> <code>static_argnums</code></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb13-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@partial</span>(jax.jit, static_argnums<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb13-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> this_recompile_everytime(shape):</span>
<span id="cb13-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> jax.numpy.zeros(shape)</span></code></pre></div></div></li>
<li><p>Compiler Driven Design</p>
<ul>
<li>Especially in JAX, partly because of its functional paradigm, framing your problem in JAX idiomatic expressions results in great speed up, sometimes more than you could do otherwise in Numba because of its design (recompile per shape, fusion/fusing compatible operations, etc.), but you’ll hit a wall if you want more low-level optimizations.</li>
<li>It can also means performance improvements can come for free through compiler improvements, as long as your code is written in JAX idiomatic way.</li>
</ul></li>
<li><p>Easy to port to GPU without setting one up.</p></li>
<li><p>JAX vs numba-cuda: The XLA compiler handles device-specific optimization automatically.</p></li>
<li><p>As a functional language, JAX nudges you to write correct code, and performance comes as a bonus. <!-- - Focus on what instead of how,
  - focus on the flow of data, 
  - (mostly) focus on the math instead of algorithm.
  - Functional programming paradigm and HPC is a rare breed. The first time I used one. --></p></li>
</ul>
<div class="notes">
<p>A few characteristics are worth internalizing. Because JAX is a tracing compiler that recompiles per shape change, anything that is genuinely a compile-time constant — a shape, for instance — has to be marked as such with <code>static_argnums</code>, or you recompile on every call.</p>
<p>More broadly, I think of this as compiler driven design. The upside is that performance improvements can arrive for free through compiler improvements, so long as your code is written the idiomatic way; the downside is the wall you hit when you want low-level control. It is a trade you make knowingly.</p>
</div>
<!-- # Bonus

## Design pattern of JIT with OOP

```py
    @cached_property
    def kalman_filtering_inplace(self) -> KalmanFilteringInplace:
        design_func = self.design_func
        grid_to_position = self.grid_to_position
        transition_func = self.transition_func
        Δx = self.Δx

        @jit(
            "(f8[:, ::1], f8[:, ::1], f8[::1], f8[::1], f8[::1])",
            nopython=True,
            nogil=True,
            cache=False,
        )
        def kalman_filtering_inplace(
            p_t_given_tMinus1: npt.NDArray[np.float64],
            p_t_given_t: npt.NDArray[np.float64],
            y: npt.NDArray[np.float64],
            params: npt.NDArray[np.float64],
            p_0: npt.NDArray[np.float64],
        ) -> None:
            """
            :param p_t_given_tMinus1: out array.
            :param p_t_given_t: out array.
            :param y: data.
            :param params: params.
            :param p_0: initial density.
            :return: p_t_given_tMinus1, p_t_given_t
            """
            size = y.shape[0]
            G = p_t_given_tMinus1.size

            # initialize
            p_t_given_tMinus1[0] = p_0

            t = 0
            y_t = y[t]
            filter_update_inplace(
                p_t_given_t[t],
                y_t,
                params,
                design_func,
                grid_to_position,
                p_t_given_tMinus1[t],
                Δx,
            )
            for t in range(1, size):
                tMinus1 = t - 1
                one_step_ahead_inplace(
                    p_t_given_tMinus1[t],
                    params,
                    transition_func,
                    grid_to_position,
                    p_t_given_t[tMinus1],
                    Δx,
                )
                y_t = y[t]
                filter_update_inplace(
                    p_t_given_t[t],
                    y_t,
                    params,
                    design_func,
                    grid_to_position,
                    p_t_given_tMinus1[t],
                    Δx,
                )

        return kalman_filtering_inplace
```

::: notes

- Point out in jit DSL, Python is the meta programming language

::: -->
</section>
<section id="when-not-to-jit-in-python" class="level2">
<h2 class="anchored" data-anchor-id="when-not-to-jit-in-python">When not to JIT in Python?</h2>
<ul>
<li>Don’t wrestle with languages, choose something else
<ul>
<li>AOT: C++ + pybind11</li>
<li>JIT: Julia</li>
</ul></li>
</ul>
<div class="notes">
<p>If you find yourself wrestling against the language, deviating from idioms and best practices, time to choose something else!</p>
<p>These are DSLs. They are small on purpose, and the moment you are fighting the smallness rather than using it, the honest move is to step outside: C++ with pybind11 if you want ahead-of-time compilation, Julia if you want a JIT.</p>
</div>
</section>
</section>
<section id="numba-vs.-jax-case-study-of-pyautolens" class="level1">
<h1>Numba vs.&nbsp;JAX: case study of PyAutoLens</h1>
<section id="benchmark-numba-vs-jax-with-1-cpu-core" class="level2">
<h2 class="anchored" data-anchor-id="benchmark-numba-vs-jax-with-1-cpu-core">Benchmark: Numba vs JAX with 1 CPU core</h2>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D_%7Bij%7D%20=%20%5Csum_%7Bk=1%7D%5EK%20%5Cfrac%7B1%7D%7Bn_k%5E2%7D%20%5Ccos%20%5Cleft(%202%20%5Cpi%20%5Cleft%5B%20%5Cleft(%5Cvec%7Bg%7D_i%20-%20%5Cvec%7Bg%7D_j%20%5Cright)%20%5Ccdot%20%5Cvec%7Bu%7D_k%20%5Cright%5D%20%5Cright)%20,%5Cquad%201%20%5Cleq%20i,%20j%20%5Cleq%20M"></p>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=64,%5Cquad%20K=32768,%5Cquad%20M%20%5Csim%20N%5E2%20%5Capprox%204000"></caption>
<thead>
<tr class="header">
<th>Implementation</th>
<th>s</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>jax_compact</code></td>
<td>2.5543 (1.00)</td>
<td>0.0050</td>
</tr>
<tr class="even">
<td><code>numba_compact</code></td>
<td>2.8768 (1.13)</td>
<td>0.0012</td>
</tr>
<tr class="odd">
<td><code>jax</code></td>
<td>3,368.6229 (&gt;1000.0)</td>
<td>1.6803</td>
</tr>
<tr class="even">
<td><code>numba</code></td>
<td>3,702.7006 (&gt;1000.0)</td>
<td>0.7385</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>Everything so far has been leading to why I actually did this work. The case study is PyAutoLens: a project with a very complicated science codebase, originally implemented in Numba, and I am part of the effort that brought the whole thing to JAX.</p>
<p>We previously have seen different implementations of <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D"> and some informal benchmarks.</p>
<p>Here we are seeing the benchmark generated using the actual library I wrote.</p>
<p>The <code>numba</code> and <code>jax</code> in this table corresponds to the low memory implementation we’ve shown previously. We see that given 1 single CPU core, the JAX version is slightly faster than the Numba version.</p>
<p>But more importantly, we see that there is a <code>_compact</code> version that I have not shown before. This is 3 orders of magnitude faster. This is done by taking advantage of the fact that δg there has a lot of repeatitive values. The algorithm that I shown to you is <img src="https://latex.codecogs.com/png.latex?O(M%5E2)"> while the compact version is <img src="https://latex.codecogs.com/png.latex?O(M)">.</p>
<p>This also illustrates that sometimes you can focus on low level optimization and have 30% speed up. Or you could spend your time instead on the mathematics behind it and find a better algorithm that can achieves unlimited amount of speed up.</p>
</div>
</section>
<section id="benchmark-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100" class="level2">
<h2 class="anchored" data-anchor-id="benchmark-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100">Benchmark: Numba with 128 CPU cores and JAX with CUDA on GPU (A100)</h2>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=32,%5Cquad%20K=8192,%5Cquad%20M%20%5Csim%20N%5E2%20%5Capprox%201000"></caption>
<thead>
<tr class="header">
<th>Implementation</th>
<th>ms</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>numba_compact</code></td>
<td>2.5029 (1.0)</td>
<td>0.0520</td>
</tr>
<tr class="even">
<td><code>jax_compact</code></td>
<td>61.5560 (24.59)</td>
<td>6.8555</td>
</tr>
<tr class="odd">
<td><code>jax</code></td>
<td>143.2451 (57.23)</td>
<td>0.0749</td>
</tr>
<tr class="even">
<td><code>numba</code></td>
<td>1,794.1949 (716.83)</td>
<td>14.9648</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>Now how about the million dollar question, how would JAX perform on the GPU? Since the Numba implementation cannot run on the GPU, let us give it a boost to have some resembance of fair fight, 128 CPU cores, vs.&nbsp;JAX on the NVidia A100.</p>
<p>(Notice the slight change of problem size here.)</p>
<p>What we see is the Numba version scales almost perfectly with 128 CPU cores. The JAX implementation is however another order of magnitude faster.</p>
<p>What happens for the <code>compact</code> case though? I think it is because they are so efficient so that the problem size is not big enough to “warm up” the GPU. But note that it is not a fair fight anyway, as we can talking about comparing benchmark from different hardware.</p>
</div>
</section>
<section id="bonus-round-1-numba-vs-jax-with-1-cpu-core-f" class="level2">
<h2 class="anchored" data-anchor-id="bonus-round-1-numba-vs-jax-with-1-cpu-core-f">Bonus round 1: Numba vs JAX with 1 CPU core (<img src="https://latex.codecogs.com/png.latex?F">)</h2>
<p><img src="https://latex.codecogs.com/png.latex?F%20=%20T%5ET%20%5Ctilde%7Bw%7D%20T"></p>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=64,%5Cquad%20B=3,%5Cquad%20K=32768,%5Cquad%20P=32,%5Cquad%20S=256">, <code>curvature_matrix</code></caption>
<thead>
<tr class="header">
<th>Implementation</th>
<th>ms</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>numba_sparse</code></td>
<td>8.0733 (1.0)</td>
<td>0.0501</td>
</tr>
<tr class="even">
<td><code>jax</code></td>
<td>19.7302 (2.44)</td>
<td>1.6986</td>
</tr>
<tr class="odd">
<td><code>jax_sparse</code></td>
<td>25.0091 (3.10)</td>
<td>0.1484</td>
</tr>
<tr class="even">
<td><code>jax_BCOO</code></td>
<td>48.5340 (6.01)</td>
<td>0.1571</td>
</tr>
<tr class="odd">
<td><code>numba_compact_sparse</code></td>
<td>49.8400 (6.17)</td>
<td>0.0794</td>
</tr>
<tr class="even">
<td><code>original_preload_direct</code></td>
<td>99.2061 (12.29)</td>
<td>0.3163</td>
</tr>
<tr class="odd">
<td><code>numba</code></td>
<td>125.3019 (15.52)</td>
<td>0.1143</td>
</tr>
<tr class="even">
<td><code>original</code></td>
<td>132.4863 (16.41)</td>
<td>0.1376</td>
</tr>
<tr class="odd">
<td><code>numba_compact_sparse_direct</code></td>
<td>139.9244 (17.33)</td>
<td>0.1562</td>
</tr>
<tr class="even">
<td><code>jax_compact_sparse_BCOO</code></td>
<td>379.7214 (47.03)</td>
<td>1.4144</td>
</tr>
<tr class="odd">
<td><code>jax_compact_sparse</code></td>
<td>380.8865 (47.18)</td>
<td>2.4322</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>Just to give you one more benchmark to look at, here is another complicated example I have done in the project. You are already familar with <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">, where as <img src="https://latex.codecogs.com/png.latex?T"> here can be a sparse matrix.</p>
<p>While I am not going into details here, but there is a bunch of combinatorics here. Should we calculate <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D"> directly, or go through the <code>compact</code> version? Should we use the sparse structure of matrix <img src="https://latex.codecogs.com/png.latex?T">, or expand that in memory first?</p>
<p>My claim is that this depends on the size of input data, and which is the fastest to choose from depends on your data which can be informed by benchmark like this.</p>
</div>
</section>
<section id="bonus-round-2-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100-f" class="level2">
<h2 class="anchored" data-anchor-id="bonus-round-2-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100-f">Bonus round 2: Numba with 128 CPU cores and JAX with CUDA on GPU (A100) (<img src="https://latex.codecogs.com/png.latex?F">)</h2>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=32,%5Cquad%20B=300,%5Cquad%20K=8192,%5Cquad%20P=32,%5Cquad%20S=256">, <code>curvature_matrix</code></caption>
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>Implementation</th>
<th>μs</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>jax</code></td>
<td>260.5957 (1.0)</td>
<td>29.3714</td>
</tr>
<tr class="even">
<td><code>jax_BCOO</code></td>
<td>3,078.2068 (11.81)</td>
<td>35.9463</td>
</tr>
<tr class="odd">
<td><code>jax_compact_sparse_BCOO</code></td>
<td>3,207.3388 (12.31)</td>
<td>107.0798</td>
</tr>
<tr class="even">
<td><code>numba_sparse</code></td>
<td>5,548.5175 (21.29)</td>
<td>64.3711</td>
</tr>
<tr class="odd">
<td><code>jax_compact_sparse</code></td>
<td>7,190.9015 (27.59)</td>
<td>35.7355</td>
</tr>
<tr class="even">
<td><code>numba</code></td>
<td>18,187.5003 (69.79)</td>
<td>5,603.6081</td>
</tr>
<tr class="odd">
<td><code>original</code></td>
<td>18,279.9851 (70.15)</td>
<td>6,052.1386</td>
</tr>
<tr class="even">
<td><code>jax_sparse</code></td>
<td>19,786.7200 (75.93)</td>
<td>42.9344</td>
</tr>
<tr class="odd">
<td><code>numba_compact_sparse</code></td>
<td>32,605.2243 (125.12)</td>
<td>248.8764</td>
</tr>
<tr class="even">
<td><code>numba_compact_sparse_direct</code></td>
<td>1,362,329.9249 (&gt;1000.0)</td>
<td>1,366.9112</td>
</tr>
<tr class="odd">
<td><code>original_preload_direct</code></td>
<td>25,218,633.7856 (&gt;1000.0)</td>
<td>8,722.4870</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>Looking at how JAX runs on the GPU, surprisingly the simplest implementation is fastest. Again I suspect there is not enough data for those faster algorithm to flex its muscle.</p>
</div>
<!-- ## Limitation of JAX on CPU

- Roughly speaking, JAX will only utilize multiple CPU cores iff it involves linear algebra
- `jax.pmap` != `numba.prange`
- set env var `NPROC` to control number of threads used (similar to `OMP_NUM_THREADS`)
- See my summary in details here: [Scientific Computing with JAX (31) | Durham HPC Days](https://blog.kolen.dev/RSE/PyAutoLens/2025-06-04-autojax.html#(31))
    - From [JAX running in CPU only mode only uses a single core](https://github.com/jax-ml/jax/issues/5022#issuecomment-1222336766):

        > This is largely working as intended at the moment. JAX doesn't parallelize operations across CPU cores unless you use explicit parallelism constructs like pmap. Some JAX operations (e.g., BLAS or LAPACK) operations have their own internal parallelism. -->
</section>
<section id="flow-chart" class="level2">
<h2 class="anchored" data-anchor-id="flow-chart">Flow chart</h2>
<p>If either Numba or JAX have enough feature to acommplish what you need, performance-wise, here’s a flowchart:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/dot/numba-vs-jax-architecture_decision_flowchart.svg" class="img-fluid figure-img" style="width:100.0%"></p>
<figcaption>Numba or JAX flowchart</figcaption>
</figure>
</div>
<div class="notes">
<p>If you have to pick between the two based on my experience, this is where each one lands. It is not the only thing you can do, but it captures the shape of the decision.</p>
<p>There is one situation where Numba is genuinely better than JAX: if you only target the CPU, you know you have a lot of CPU cores available, and your problem is not linear-algebra heavy. In that corner Numba wins. Most of the time, though, JAX is quite good.</p>
</div>
</section>
</section>
<section id="what-jit-has-enabled-in-scientific-computing" class="level1">
<h1>What JIT has enabled in scientific computing?</h1>
<section id="context-maximal-likelihood-estimation-mle" class="level2">
<h2 class="anchored" data-anchor-id="context-maximal-likelihood-estimation-mle">Context: Maximal Likelihood Estimation (MLE)</h2>
<ul>
<li><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D,%20T,%20F">, etc. are part of a likelihood function</li>
<li>Aim: seek the input parameters corresponding to maximum likelihood
<ul>
<li><img src="https://latex.codecogs.com/png.latex?%5CRightarrow"> peak finding</li>
<li>Gradient information is going to be useful here.</li>
</ul></li>
<li>JAX another secret weapon: autodiff / autograd (<code>jax.grad</code>)
<ul>
<li>compilers can transform code</li>
<li>transform function to find its gradient automatically</li>
</ul></li>
</ul>
<div class="notes">
<p>Now, let’s see what JIT has enabled our science.</p>
<p>I am not going to give these graphs a justice. But, I am going to have a crash course here and jump to conclusion. The <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D,%20T,%20F">, etc. you have seen before is part of a calculation known as the likelihood function. The <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D"> is a big part of turning the observed data into a likelihood function. Now you can make predictions from models given some parameters. and the likelihood function is going to tell you how likely those parameters are. Using different kinds of samplers, which all involve running the likelihood functions repeatedly, often with <img src="https://latex.codecogs.com/png.latex?O(100,000)"> of iterations. And the parameters that corresponds to the maximum likelihood is simply called the maximal likelihood estmator (MLE).</p>
<p>This is basically how we do inverse problem statistically. And obviously it is some kind of maximization problem.</p>
<p>There is one thing about JAX I have not told you yet, and it belongs here. If you are looking for a peak, gradient information is obviously useful: the gradient already tells you roughly where the peak is, pointing you in the right direction. And now that things are jit-compiled, consider what compilers actually do — they transform code. You have code written one way and it is transformed into another. So why not leverage that to transform your function into its gradient as well? That is exactly what automatic differentiation, <code>jax.grad</code>, does, and in a science case like this it is a real advantage.</p>
</div>
</section>
<section id="mle-in-action" class="level2">
<h2 class="anchored" data-anchor-id="mle-in-action">MLE in action</h2>
<!-- The deck lays these out in columns; the article wants captioned figures
     and a subfigure group that survives LaTeX. Same images, two presentations. -->
<ul>
<li>25 free parameters
<ul>
<li>Lens Light (11): Sersic + Exponential</li>
<li>Lens Mass (7): SIE + Shear</li>
<li>Source Light (7): Sersic</li>
</ul></li>
</ul>
<p>PyAutoLens (via PyAutoFit) supports Nested sampling (<strong>Dynesty</strong>), MCMC (emcee), particle swarm optimization (PySwarms)</p>
<div id="fig-lens" class="quarto-layout-panel">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-lens-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/image7.png" class="img-fluid figure-img"></p>
<figcaption>The lens light.</figcaption>
</figure>
</div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/image3.png" class="img-fluid figure-img"></p>
<figcaption>The observation: a strong gravitational lensing pattern.</figcaption>
</figure>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-lens-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: What the 25-parameter model has to reproduce.
</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/image25.gif" class="img-fluid figure-img" style="width:67.0%"></p>
<figcaption>As the sampler iterates, the predicted image in the top right corner gradually comes to resemble the observation.</figcaption>
</figure>
</div>
<div class="notes">
<p>Here, we will see a concrete example of how PyAutoLens does it. It is a bit dense, but focus on the image on the right: this is your observed data, and it is an image of a strong gravitational lensing. The question to ask is, out of all those complicated parameters, how would you come up with a model that predicts exactly that pattern?</p>
<p>The animation shows how iterations of different parameters converge on that pattern.</p>
</div>
<div class="notes">
<p>At the start, the image in the top right hand corner does not really resemble the observation. But as it iterates more and more, it gradually becomes it. Eventually, comparing the image in the top right hand corner against the data, we can see the modeled image pretty much ressemble it.</p>
<p>Here is how we can infer on the mass of the dark matter we cannot see.</p>
<p>One more thing you can notice here is, while probably I can implement the likelihood function in any language I want, eventually I need to expose it in a Python interface because I need to pass it to these libraries: Nested sampling (<strong>Dynesty</strong>), MCMC (emcee), particle swarm optimization (PySwarms).</p>
<p>This is what I mean when I say the superpowers of Python is it is where the community is, and why which language we implement it does not matter. Python is still our favorite glue. With JIT in Python like Numba and JAX, we can stick to the glue a bit longer.</p>
</div>
</section>
<section id="so-who-won" class="level2">
<h2 class="anchored" data-anchor-id="so-who-won">So, who won?</h2>
<div class="notes">
<p>So in this particular case — PyAutoLens — who won the battle of JIT? Can you guess?</p>
<p>JAX, of course. Our project was funded to move things from Numba to JAX; if JAX had lost, we would basically be a complete failure. The interesting question is by how much. The room guessed five times.</p>
<p>It is <strong>50x</strong>, according to preliminary results. On the slide that number is set fifty times larger than the text around it, which is roughly the point — I don’t feel good about the typography, but that is how much.</p>
</div>
</section>
<section id="team-links-references" class="level2">
<h2 class="anchored" data-anchor-id="team-links-references">Team, Links, &amp; References</h2>
<!-- The QR code is deck-only; it is meaningless in prose. -->
<ul>
<li>PyAutoLens Team
<ul>
<li>James W. Nightingale</li>
<li>Richard G. Hayes</li>
<li>Aristeidis Amvrosiadis</li>
<li>Coleman Krawczyk</li>
<li>Gokmen Kilic</li>
<li>…</li>
</ul></li>
<li>Link of this project: <a href="https://ickc.github.io/python-autojax/" class="uri">https://ickc.github.io/python-autojax/</a>. This is a standalone framework to compare different implementations of functions. Functions are being upstreamed to <a href="https://github.com/Jammy2211/pyautolens">PyAutoLens</a>.
<ul>
<li><a href="https://github.com/ickc/python-autojax/blob/main/examples/w_tilde_pycon25.py">Link to example used today</a></li>
<li>For different ways to do reduced sum efficiently in JAX, see <a href="https://github.com/ickc/python-autojax/tree/main/experiments/reduced_sum">python-autojax/experiments/reduced_sum at main · ickc/python-autojax</a> for more.</li>
<li><a href="https://blog.kolen.dev/RSE/PyAutoLens/2025-06-04-autojax.html">Scientific Computing with JAX | Durham HPC Days</a></li>
</ul></li>
</ul>
<div class="notes">
<p>Lastly, I just want to point you to the team of people working on this and some links to share. Thank you.</p>
</div>


</section>
</section>

 ]]></description>
  <category>Public talk</category>
  <category>JAX</category>
  <category>JWST</category>
  <category>PyCon UK</category>
  <guid>https://blog.kolen.dev/RSE/PyAutoLens/2025-09-21-autojax-article.html</guid>
  <pubDate>Sun, 21 Sep 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Durham HPC Days 2025</title>
  <dc:creator>Kolen Cheung</dc:creator>
  <link>https://blog.kolen.dev/HPC/Durham-HPC-Days/2025-article.html</link>
  <description><![CDATA[ 





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><embed src="./2025.html" style="width:100.0%" height="400"></p>
<figcaption><a href="./2025.html">Slide to the talk</a></figcaption>
</figure>
</div>
<section id="introduction" class="level1">
<h1>Introduction</h1>
<section id="durham-hpc-days" class="level2">
<h2 class="anchored" data-anchor-id="durham-hpc-days">Durham HPC Days</h2>
<ul>
<li>From Durham University</li>
<li>Strong ties with DiRAC HPC facility</li>
<li>Seem to always be at the week predating the ISC Conference</li>
</ul>
</section>
<section id="theme-ai-and-simulations-we-all-need-hpc" class="level2">
<h2 class="anchored" data-anchor-id="theme-ai-and-simulations-we-all-need-hpc">Theme: AI and simulations — We all need HPC</h2>
<p>Officially,</p>
<blockquote class="blockquote">
<p>there is no gap between HPC and AI: HPC underpins progress in both simulations — high-performance and high throughput — and AI</p>
</blockquote>
<p>My general observation of the key themes are:</p>
<ul>
<li>Big themes:
<ul>
<li>GPU</li>
<li>AI</li>
</ul></li>
<li>Smaller themes:
<ul>
<li>Benchmarking</li>
<li>People (RSE)</li>
<li>Green</li>
</ul></li>
</ul>
</section>
</section>
<section id="keynotes" class="level1">
<h1>Keynotes</h1>
<section id="the-changing-shape-of-science-funding-in-the-u.s." class="level2">
<h2 class="anchored" data-anchor-id="the-changing-shape-of-science-funding-in-the-u.s.">The changing shape of science funding in the U.S.</h2>
<div class="columns">
<div class="column" style="width:50%;">
<p>Rich Knepper, Chair of the Coalition for Academic Scientific Computation (CASC)</p>
<ul>
<li>Spent about 1 slide per month to document what impacted science in the US since the new administration took place in Jan 20th, 2025.</li>
<li>Among strategies to deal with budget/funding cuts, some universities ask their researchers to continue use the fund and prepare to fight it in the court.</li>
<li>Cited a paper studying the Impact of World War II on German Science, which can have decades long impact.</li>
<li>Other countries are luring US scientists, e.g.&nbsp;€500 million from EU</li>
<li>Feel great to be here in the UK!</li>
</ul>
</div><div class="column" style="width:50%;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/HPC/Durham-HPC-Days/National-Science-Foundation-grant-funding-through-May-21.png" class="img-fluid figure-img"></p>
<figcaption><a href="https://www.nytimes.com/interactive/2025/05/22/upshot/nsf-grants-trump-cuts.html">National Science Foundation grant funding through May 21</a></figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="finding-the-fulcrum-rethinking-supercomputing-at-scale" class="level2">
<h2 class="anchored" data-anchor-id="finding-the-fulcrum-rethinking-supercomputing-at-scale">Finding the Fulcrum: Rethinking Supercomputing at Scale</h2>
<p>Cristin Merritt (Chief Marketing Officer - Alces Flight Ltd.)</p>
<ul>
<li>A reflection on what she learnt in 2024, launching Move the Needle, a year-long initiative advancing workforce inclusivity in HPC</li>
<li>Emphasize on the people in the HPC community</li>
<li>As demands, technologies, etc. changes, rebalancing is key</li>
<li>Technical skill is represented by the stand</li>
<li>Soft skill probably is the beam, or one-side of the beam</li>
</ul>
</section>
<section id="molecular-simulation-in-process-engineering-impressions-from-the-era-of-exascale-computing-and-data-science" class="level2">
<h2 class="anchored" data-anchor-id="molecular-simulation-in-process-engineering-impressions-from-the-era-of-exascale-computing-and-data-science">Molecular Simulation in Process Engineering: Impressions from the Era of Exascale Computing and Data Science</h2>
<p>Prof Philipp Neumann</p>
<p>This involves topics such as load balancing, automated algorithm selection and coupled multiscale systems, all of which have been explored and covered in the open-source software packages ls1 mardyn, AutoPas and MaMiCo.</p>
</section>
<section id="the-uks-digital-research-infrastructure" class="level2">
<h2 class="anchored" data-anchor-id="the-uks-digital-research-infrastructure">The UK’s Digital Research Infrastructure</h2>
<p>Presenter: Afia Masood (UKRI)</p>
<p>Kick-off: The UK’s Knowledge Exchange Grant and Accelerate Computing initiatives</p>
<p>Convener: Helen Cooper, Nick Brown, Tobias Weinzierl</p>
<ul>
<li>Knowledge Exchange DRI grant</li>
<li>two DRI accelerate computing grants
<ul>
<li><a href="https://www.jiscmail.ac.uk/cgi-bin/webadmin?SUBED1=ACIT&amp;A=1">Accelerated Compute Infrastructure Training (ACIT)</a></li>
<li><a href="https://shareing-dri.github.io/about/vision">SHAREing</a></li>
</ul></li>
</ul>
</section>
<section id="unleash-the-control-freak-in-yourself-for-fun-and-profit-and-for-science" class="level2">
<h2 class="anchored" data-anchor-id="unleash-the-control-freak-in-yourself-for-fun-and-profit-and-for-science">Unleash the control freak in yourself for fun and profit — and for science!</h2>
<div class="columns">
<div class="column" style="width:50%;">
<ul>
<li>Slow code is easy to scale: use an example of strong scaling comparison between <code>-O0</code> and <code>-O2</code> (?) flag to demonstrates while the scaling of the latter looks worse, it is still faster than the former in absolute time.</li>
<li>Should spent more effort in finding out why, see “prime number effect” for example.</li>
<li><a href="https://github.com/RRZE-HPC/MachineState/tree/master">MachineState</a> provides a systematic approach to gather as many performance influencing factors as known to the performance engineering community.
<ul>
<li>“more and more conferences and journals request artifact descriptions along with the paper to improve the reproducibility of research”</li>
</ul></li>
<li>His opinion is that paper without said practices should not be accepted, with the context however applied to Computer Scientists only to maintain inclusivity with RSE.</li>
</ul>
</div><div class="column" style="width:50%;">
<p>Thomas Gruber (Regionales RechenZentrum Erlangen - RRZE)</p>
<blockquote class="blockquote">
<p>We find that if the number of processes is prime, SpecI2M fails to work properly, which we can attribute to short inner loops emerging from the one-dimensional domain decomposition in this case.</p>
</blockquote>
<ul>
<li>Has been rejected twice</li>
</ul>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/HPC/Durham-HPC-Days/laukemannCloverLeafIntelMultiCore2024-fig2.png" class="img-fluid figure-img"></p>
<figcaption>“Prime number effect” <span class="citation" data-cites="laukemann_cloverleaf_2024">(fig.&nbsp;2, Laukemann et al. 2024)</span></figcaption>
</figure>
</div>
</div>
</div>
</section>
<section id="challenges-and-opportunities-in-hpc-for-numerical-relativity-nr" class="level2">
<h2 class="anchored" data-anchor-id="challenges-and-opportunities-in-hpc-for-numerical-relativity-nr">Challenges and Opportunities in HPC for Numerical Relativity (NR)</h2>
<p><a href="https://kaclough.github.io/Research.html">Dr Katy Clough</a> (STFC Ernest Rutherford Research Fellow)</p>
<ul>
<li><a href="https://github.com/GRTLCollaboration/GRTeclyn">GRTeclyn</a>
<ul>
<li>A “lucky” experience of porting GR code using the AMReX framework which utilizes the GPU efficiently to solve wave equations.</li>
<li>GR can use off-the-shelf library is that GR can be rewritten as an initial value problem looks very much like a wave equation, and with the help of a gauge, without degeneracy such that it can be solved numerically.</li>
<li>While high curvature near the sigularity is a problem, since there’s no information flowing back from within the event horizon, you can afford garbage solution near within the event horizon and just mask out the singularity!</li>
<li>signatures:
<ul>
<li>merging of 2 black holes, which has direct observational science implications
<ul>
<li>proxies such as analytical model fitting is built by calibrating via NR to facilitate Bayesian Inference.</li>
</ul></li>
<li>alien warp drives: NR can predicts the signature of warp drive engine failure! Author lements the current generations of young people not watching Star Trek enough. The frequency scale is not common in other kind of science so it is pretty hopeless that we’d build an experiment to observe such!</li>
</ul></li>
</ul></li>
<li>Avocates the importance of RSE helps (4 RSEs for this porting), and more people should be made known to the resource available. She only knew because she has access to DiRAC and other colleagues told her about a call.</li>
<li>UK community for NR: <a href="https://www.uknumericalrelativity.org/">UKNR</a></li>
</ul>
</section>
</section>
<section id="symposium" class="level1">
<h1>Symposium</h1>
<section id="the-uk-centre-of-excellence-coe-for-the-characterisation-and-co-design-of-systems-hardware-and-enabling-software-shes" class="level2">
<h2 class="anchored" data-anchor-id="the-uk-centre-of-excellence-coe-for-the-characterisation-and-co-design-of-systems-hardware-and-enabling-software-shes">The UK Centre of Excellence (CoE) for the Characterisation and Co-Design of Systems, Hardware and Enabling Software (SHES)</h2>
<ul>
<li>Goal: maximize ROI of HPC by a continuous improvement process with these systems, dynamically adapting and characterising them to suit ever-changing workloads and demands</li>
</ul>
</section>
<section id="benchmarking-of-hpc-systems-for-simulation-and-ai" class="level2">
<h2 class="anchored" data-anchor-id="benchmarking-of-hpc-systems-for-simulation-and-ai">Benchmarking of HPC systems for simulation and AI</h2>
<p>Conveners: DiRAC, ExCALIBUR, UKRI Living Benchmarks Lead: Mark Wilkinson</p>
<p>Use science benchmarks to optimise the design of large-scale computing services</p>
<ul>
<li>the ExCALIBUR Benchmarking project</li>
<li>the ExCALIBUR BASE-II project</li>
<li><a href="https://ukri-bench.github.io">the UKRI Living Benchmarks project</a>: curating open-source benchmarks that support the procurement and performance assessment of high-performance computing systems</li>
<li>the DiRAC-4 design process
<ul>
<li>Mark emphasized the continuous benchmarking of the DiRAC system (Cosma 8?) from procurement to deployment. Its performance has increased by 2x in the process by efforts from RSE. He avocates for a model similar to the US, where half the funding goes to the machine, half goes to the people.</li>
</ul></li>
</ul>
</section>
</section>
<section id="submitted-talks" class="level1">
<h1>Submitted talks</h1>
<section id="isambard-ai-and-isambard-3-democratising-the-user-experience-for-ai-and-simulation-hpc" class="level2">
<h2 class="anchored" data-anchor-id="isambard-ai-and-isambard-3-democratising-the-user-experience-for-ai-and-simulation-hpc">Isambard-AI and Isambard 3: Democratising the User Experience for AI and simulation HPC</h2>
<p>Who: Richard Gilham, Bristol Centre for Supercomputing</p>
</section>
<section id="the-hpc-hardware-lab-at-durham-university" class="level2">
<h2 class="anchored" data-anchor-id="the-hpc-hardware-lab-at-durham-university">The HPC Hardware Lab at Durham University</h2>
<p>Who: Alastair Basden, Durham University</p>
<ul>
<li>The Durham HPC Hardware Lab is hosted by the DiRAC COSMA HPC facility and provides UK researchers with access to cutting edge technologies and facilities, to allow testing of codes, software migration to new hardware, and study of new paradigms.</li>
<li>One thing I remember from it is how they set up two identical systems with a different interconnects to study the difference just between the two technologies of the interconnects.</li>
</ul>
</section>
<section id="commissioning-aire-a-new-hpc-system-at-the-university-of-leeds" class="level2">
<h2 class="anchored" data-anchor-id="commissioning-aire-a-new-hpc-system-at-the-university-of-leeds">Commissioning Aire, a new HPC system at The University of Leeds</h2>
<p>Who: Andrew Harvie, University of Leeds</p>
<ul>
<li>IIRC, part of the challenge is to navigate how to frame it as an upgrade to retiring systems, not a new system</li>
</ul>
</section>
<section id="delivering-training-with-a-mini-hpc-built-from-raspberry-pis" class="level2">
<h2 class="anchored" data-anchor-id="delivering-training-with-a-mini-hpc-built-from-raspberry-pis">Delivering Training with a mini HPC built from Raspberry Pis</h2>
<p>Who: Jannetta Steyn, Senior Research Software Engineer, Head of Training and Community, Newcastle University</p>
<ul>
<li>Build a mini-HPC system with 7 Raspberry Pi</li>
<li>While similar prior work exists, this project aims to make it reproducible (from software to hardware to 3D printed housing to “blueprint”)</li>
<li>Experimented with alternative SBC with funding, but eventually replace it with all the RPis she can find in her home, because RPi is better documented and supported</li>
<li>Primarily for educating people to build HPC without £££ (i.e.&nbsp;lowering entry barrier)
<ul>
<li>Much easier to see the effect of, say, overloading the cluster and see the effects on another workload</li>
</ul></li>
<li>Would love to have access to GPU in the future, but suffices for now</li>
</ul>
</section>
<section id="driving-energy-efficiency-of-operation-with-wind-turbine-modelling" class="level2">
<h2 class="anchored" data-anchor-id="driving-energy-efficiency-of-operation-with-wind-turbine-modelling">Driving energy efficiency of operation with wind turbine modelling</h2>
<p>Who: Nick Brown, EPCC</p>
<p>IIRC, discussed using RISC-based GPU-like accelerator to demonstrates better energy efficiency comparing to traditional CPU</p>
</section>
<section id="ai-for-green-hpc-how-machine-learning-is-transforming-energy-efficiency" class="level2">
<h2 class="anchored" data-anchor-id="ai-for-green-hpc-how-machine-learning-is-transforming-energy-efficiency">AI for Green HPC: How Machine Learning is Transforming Energy Efficiency</h2>
<p>Who: Fawada Qaiser, Durham University</p>
<p>Case studies from leading supercomputing facilities highlight how reinforcement learning and neural networks enhance power-aware job scheduling, cooling management, and dynamic power adjustments for CPUs, GPUs, and accelerators.</p>
</section>
<section id="hpc-waste-heat-storage-the-ichs-project-at-durham-university" class="level2">
<h2 class="anchored" data-anchor-id="hpc-waste-heat-storage-the-ichs-project-at-durham-university">HPC waste heat storage: the ICHS project at Durham University</h2>
<p>Who: Paul Walker, Durham University</p>
<ol type="1">
<li>HPC immersion tank</li>
<li>exploring the use of flooded mine workings beneath the data centre</li>
</ol>
</section>
<section id="advancing-cats-the-climate-aware-task-scheduler-for-hpc-and-htc-application" class="level2">
<h2 class="anchored" data-anchor-id="advancing-cats-the-climate-aware-task-scheduler-for-hpc-and-htc-application">Advancing CATS, The Climate Aware Task Scheduler, for HPC and HTC application</h2>
<p>Who: Sadie Bartholomew, NCAS</p>
<ul>
<li><a href="https://github.com/GreenScheduler/cats">Climate-Aware Task Scheduler</a>, which schedules tasks to minimise the total estimated carbon intensity of the electricity grid for the job duration using real-time data from the UK’s National Grid ESO API</li>
<li>Version 1 (current): <code>at</code> command, targeted smaller-scale tasks on local machines</li>
<li>Version 2 (future): integrate with Slurm</li>
</ul>
</section>
<section id="benchmarking-ml-applications" class="level2">
<h2 class="anchored" data-anchor-id="benchmarking-ml-applications">Benchmarking ML applications</h2>
<p>Who: Adrian Jackson, EPCC</p>
<p>Discussed various challenges and solutions to benchmark ML applications due to project delay</p>
</section>
<section id="having-it-all-can-software-be-portable-performant-and-productive" class="level2">
<h2 class="anchored" data-anchor-id="having-it-all-can-software-be-portable-performant-and-productive">Having it all: Can software be portable, performant and productive?</h2>
<p>Who: Chris Maynard, Met Office</p>
<p>DSL, Gung Ho, PSyclone, and LFRic.</p>
</section>
<section id="scientific-computing-with-jax-a-case-study-evaluating-gravitational-lensing-likelihood" class="level2">
<h2 class="anchored" data-anchor-id="scientific-computing-with-jax-a-case-study-evaluating-gravitational-lensing-likelihood">Scientific Computing with JAX: A Case Study Evaluating Gravitational Lensing Likelihood</h2>
<p>Who: Kolen Cheung, University of Exeter</p>
<ul>
<li><a href="../../RSE/PyAutoLens/2025-06-04-autojax.html">Link</a></li>
<li>Expanded upon that I presented in the <a href="../../RSE/PyAutoLens/2025-03-26-autojax.html">technical</a></li>
<li>Conclude that JAX is not good on multithreading on CPU, i.e.&nbsp;JAX is not good for traditional HPC with CPUs only</li>
<li>Interesting interactions from others:
<ul>
<li>Why JAX is bad on multithreading on CPU? Probably just a lack of interested from primarily machine learning focused community (who wouldn’t have access to GPU/TPU?)</li>
<li>One like to use JAX for things like einsum a lot, and concerns about the performance implications. My feedback was
<ul>
<li>It can be beneficial if users have access to GPU</li>
<li>Possibly keep a few similar implementations for different target systems</li>
<li>Profile it if performance is a concern, including the jit-lag if recompile per shape change is a concern</li>
<li>Think about long term requirements (or the lack of), scale of the project, etc.</li>
</ul></li>
<li>Another RSE is very concerned as PI(s) are pushing to port to JAX as their collegues have great experience
<ul>
<li>JAX is not the answer to everything (and point out limitations mentioned in my talk)</li>
<li>He don’t want to port 80k lines of C++ to another framework, would rather implement autodiff/autograd in that code base (Good luck!)</li>
</ul></li>
<li>Discussed other related things such as Cython, Julia, etc.</li>
</ul></li>
</ul>
</section>
<section id="simulating-discrete-event-systems-on-hpc-sleptsov-net-case-study" class="level2">
<h2 class="anchored" data-anchor-id="simulating-discrete-event-systems-on-hpc-sleptsov-net-case-study">Simulating Discrete-Event Systems on HPC: Sleptsov Net Case Study</h2>
<p>Who: Dmitry Zaitsev, University of Derby</p>
<ul>
<li>Sleptsov nets
<ul>
<li>Turing-complete</li>
<li>parallel algorithm for Discrete-event systems (DES)</li>
</ul></li>
<li>Very unapproachable</li>
<li>Often ask strange questions to other speakers
<ul>
<li>recapturing heat of data centre and turn that into electricity to power data centre, described as perpetual motion machine by a panel speaker</li>
<li>(To be fair of course he meant recapturing some energy and reduces waste)</li>
</ul></li>
</ul>
</section>
<section id="gpu-offloads-for-gravity-calculations-in-swift-cosmology-code" class="level2">
<h2 class="anchored" data-anchor-id="gpu-offloads-for-gravity-calculations-in-swift-cosmology-code">GPU offloads for gravity calculations in SWIFT cosmology code</h2>
<p>Who: Sarah Johnston, Durham University</p>
<ul>
<li>Praised by others to be a very approachable presentation even to outsiders</li>
<li>Focused on porting the part of the simulation that involve gravity, accounted for ~60% of workload</li>
<li>The original CPU code has 3 terms: particle-particle, particle-multipole, multipole-multipole.</li>
<li>The GPU code dropped the particle-multipole code with better precision
<ul>
<li>Why? Because more particles are included in the particle-particle term as GPU has many larger parallelism available</li>
</ul></li>
<li>This code is memory intensive, benefits of porting fully to the GPU is unclear. But it is started as people saying it is important to port to the GPU (probably PhD advisor(s) is primarily driving it forward?)</li>
<li>IIRC, currently it (the GPU implementation) is slower than the CPU (implementation)</li>
</ul>
</section>
</section>
<section id="dirac-rses" class="level1">
<h1>DiRAC RSEs</h1>
<section id="dirac-rse-support-for-swift" class="level2">
<h2 class="anchored" data-anchor-id="dirac-rse-support-for-swift">DiRAC RSE support for SWIFT</h2>
<p>Gokmen Kilic (Durham)</p>
<ul>
<li>I thought the SWIFT programming language is surprisingly having a presence in HPC</li>
<li>SWIFT stands for SPH With Inter-dependent Fine-grained Tasking, where SPH stands for Smoothed Particle Hydrodynamics.</li>
</ul>
</section>
<section id="parallel-neighbour-finding-algorithm" class="level2">
<h2 class="anchored" data-anchor-id="parallel-neighbour-finding-algorithm">Parallel neighbour finding algorithm</h2>
<p>Nicolin Govender (UCL)</p>
<p>Key message is that neighbour finding is common in many different domains, and there’s a potential benefits to have a general library with parallel algorithm to unify efforts.</p>
</section>
<section id="thoughts-about-mixed-precision" class="level2">
<h2 class="anchored" data-anchor-id="thoughts-about-mixed-precision">Thoughts about mixed precision</h2>
<p>Simon Burbidge (Leicester)</p>
<p>Key message is that mixed precision is great and people should start to explore.</p>
</section>
</section>
<section id="workshops" class="level1">
<h1>Workshops</h1>
<section id="hep-generative-ai-for-lattice-qcd-calculations" class="level2">
<h2 class="anchored" data-anchor-id="hep-generative-ai-for-lattice-qcd-calculations">HEP: Generative AI for Lattice QCD calculations</h2>
<p>Gurtej Kanwar (Edinburgh)</p>
</section>
<section id="hep-determining-the-structure-of-the-proton-with-machine-learning" class="level2">
<h2 class="anchored" data-anchor-id="hep-determining-the-structure-of-the-proton-with-machine-learning">HEP: Determining the structure of the proton with Machine Learning</h2>
<p>Roy Stegeman (Edinburgh)</p>
<ul>
<li>Solve the inverse problem of extracting the parton distribution functions from a finite set of data</li>
<li>One of the key I can remember is that it is possible to validate the output by checking symmetry requirements of the solutions, which is key to design the ML model</li>
<li>They didn’t have the chance to discuss the ML model at all due to time constraints</li>
</ul>
</section>
<section id="hep-physics-focused-system-design" class="level2">
<h2 class="anchored" data-anchor-id="hep-physics-focused-system-design">HEP: Physics-focused system design</h2>
<p>Antonin Portelli (Edinburgh)</p>
<blockquote class="blockquote">
<p>I will summarise how lattice QCD benchmarks, based on the Grid library, were used during the procurement process as well as for optimising system energy efficiency in production.</p>
</blockquote>
</section>
<section id="benchmarking-the-reframe-framework" class="level2">
<h2 class="anchored" data-anchor-id="benchmarking-the-reframe-framework">Benchmarking: The Reframe framework</h2>
<p>Tuomas Koskela (UCL)</p>
<ul>
<li><a href="https://reframe-hpc.readthedocs.io/en/stable/">ReFrame</a></li>
</ul>
</section>
<section id="performance-modelling-of-detrimental-task-execution-patterns-in-mainstream-openmp-runtimes" class="level2">
<h2 class="anchored" data-anchor-id="performance-modelling-of-detrimental-task-execution-patterns-in-mainstream-openmp-runtimes">Performance Modelling of Detrimental Task Execution Patterns in Mainstream OpenMP Runtimes</h2>
<p>Adam Tuft (Durham)</p>
<p><span class="citation" data-cites="espinosa_detrimental_2024">Tuft et al. (2024)</span></p>
<blockquote class="blockquote">
<p>While [OpenMP] provides descriptive and prescriptive annotations, it is in many places deliberately unspecific how to implement its annotations.</p>
<p>… “quasi-standard” reference behaviour introduces performance flaws.</p>
<p>… we propose prescriptive clauses to constrain the OpenMP implementations.</p>
</blockquote>
</section>
<section id="numerical-relativity-mhduet-modelling-general-relativistic-mhd-on-cpugpu-architectures" class="level2">
<h2 class="anchored" data-anchor-id="numerical-relativity-mhduet-modelling-general-relativistic-mhd-on-cpugpu-architectures">Numerical Relativity: MHDuet: Modelling General Relativistic MHD on CPU/GPU Architectures</h2>
<blockquote class="blockquote">
<p>MHDuet is an automatically generated, efficient computational code designed to simulate the dynamics of strongly gravitating, high-density matter in astrophysical scenarios involving compact objects such as black holes and neutron stars.</p>
</blockquote>
<blockquote class="blockquote">
<p>… it is currently being ported to AMReX to exploit the capabilities of modern GPU-accelerated and massively parallel systems</p>
</blockquote>
<blockquote class="blockquote">
<p>… solves the equations of general relativistic magnetohydrodynamics (GRMHD)</p>
</blockquote>
</section>
<section id="numerical-relativity-improving-eccentric-gravitational-waveform-models-with-numerical-relativity" class="level2">
<h2 class="anchored" data-anchor-id="numerical-relativity-improving-eccentric-gravitational-waveform-models-with-numerical-relativity">Numerical Relativity: Improving eccentric gravitational waveform models with Numerical Relativity</h2>
<p>Alice Bonino (University of Birmingham)</p>
<blockquote class="blockquote">
<p>… whilst these provide an accurate description of the gravitational-wave signals throughout the inspiral, they are only valid up to moderate eccentricities and are not reliable as the binary approaches merger.</p>
</blockquote>
<blockquote class="blockquote">
<p>… appeal to Numerical Relativity simulations to help model the complete inspiral-merger-ringdown signal from eccentric binaries.</p>
</blockquote>
</section>
<section id="numerical-relativity-automated-kernel-generation-for-the-numerical-relativity-solver-exagrype" class="level2">
<h2 class="anchored" data-anchor-id="numerical-relativity-automated-kernel-generation-for-the-numerical-relativity-solver-exagrype">Numerical Relativity: Automated Kernel Generation for the Numerical Relativity Solver ExaGRyPE</h2>
<p>Timothy Stokes (Durham University)</p>
<ul>
<li><a href="https://tobiasweinzierl.webspace.durham.ac.uk/software/exahype/">ExaHyPE</a> is a numerical engine used to solve hyperbolic PDE systems
<ul>
<li><a href="https://tobiasweinzierl.webspace.durham.ac.uk/exagrype/">ExaGRyPE</a> is NR based on ExaHyPE</li>
</ul></li>
<li>ExaHyPe-DSL eCSE project which replaces these manual kernels with those written using a Domain Specific Language (DSL)
<ul>
<li>think PSyclone</li>
<li>Python DSL <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> Python AST <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> <a href="https://www.epcc.ed.ac.uk/whats-happening/articles/exahype-dsl-project-integrating-exahype-engine-llvm-compiler-ecosystem">ExCALIBUR xDSL</a> toolkit <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> High-Level MLIR <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> … <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> Lower-Level MLIR Dialects <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> LLVM-IR <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> Machine Code</li>
<li>complicated build system with make to expose it back as Python module</li>
<li>we have a nice dicussion about if this is a right abstraction, and also mention a possible Cython-style jit-like experience in Notebook</li>
<li>he also mention if he’d do it again, he’d not use the Python AST as there are edge cases to fix, better implement his own parser instead.</li>
</ul></li>
</ul>
</section>
<section id="cosec-creating-a-cohesive-distributed-digital-research-infrastructure" class="level2">
<h2 class="anchored" data-anchor-id="cosec-creating-a-cohesive-distributed-digital-research-infrastructure">CoSeC &amp; Creating a cohesive distributed Digital Research Infrastructure</h2>
<p>Conveners: Stephen Longshaw (UKRI STFC), Damian Jones (UKRI STFC)</p>
<p>13:30 - 15:00 (talks) and 15:30 - 16:30 (panel) Talks</p>
<ul>
<li><p>Applied AI for the Digital Humanities (CCP-AHC; Karina Rodriguez Echavarria / Jeyan Thiyagalingam)</p></li>
<li><p>Computational Biology (Martyn Winn – joining remotely)</p></li>
<li><p>Computational Engineering (CCP-NTH, CCP-Turbulence, UKTC; Wei Wang)</p></li>
<li><p>Computational Materials and Molecular Science (Marcello Puligheddu / Rajany RV)</p></li>
<li><p>Panel discussion:</p>
<blockquote class="blockquote">
<p>The basic idea is to discuss how (and whether?) it is possible for us to create a DRI using the distributed funding approach – i.e.&nbsp;funding the infrastructure as lots of smaller projects (relatively) that need to interoperate to be a single cohesive overall infrastructure.</p>
</blockquote>
<ul>
<li><p>Mark commented the “80% rule” does not work well for project like DiRAC as it would not make sense for one university to pay 20% overhead for all collaborations.</p></li>
<li><p>Some call that is not for early researchers often requires people to have experience in the field. For those people sensing their field is shrinking as there’s a loss of interest, they often find themselves rejects after rejects in calls.</p></li>
<li><p>Small funds (~£5,000) and large funds (~£100,000) have big gaps. Something in between is needed.</p></li>
<li><p>Too many different kinds of funds, might need to be consolidated.</p></li>
</ul></li>
</ul>
</section>
</section>
<section id="ukri-dri" class="level1">
<h1>UKRI DRI</h1>
<section id="supporting-digital-research-technical-professionals-drtps-projects-opportunities-and-challenges" class="level2">
<h2 class="anchored" data-anchor-id="supporting-digital-research-technical-professionals-drtps-projects-opportunities-and-challenges">Supporting digital Research Technical Professionals (dRTPs): Projects, opportunities and challenges</h2>
<ul>
<li><a href="https://www.universe-hpc.ac.uk/">UNIVERSE-HPC</a> will define a training curriculum framework – spanning from undergraduate to continuing professional development level - for Research Software Engineers (RSEs) specializing in high performance computing (HPC).</li>
<li>two Network+ projects:
<ul>
<li>CHARTED</li>
<li>SCALE-UP</li>
</ul></li>
<li>projects:
<ul>
<li><a href="https://step-up.ac.uk/about/">STEP-UP</a> - A Strategic TEchnical Platform for University Technical Professionals. Supporting “digital Research Technical Professionals” (dRTPs) and researchers working with research software, research data and research computing infrastructure, in the <strong>London region</strong> and beyond.</li>
<li>DRIFT is focused on the training for research facilitators and teams</li>
<li><a href="https://www.ccpahc.ac.uk/">CCP-AHC</a>: Collaborative Computational Project (CCP) serving Arts, Humanities, and Culture researchers
<ul>
<li><a href="https://forms.office.com/pages/responsepage.aspx?id=i9hQcmhLKUW-RNWaLYpvlOK6KoSUl1tDge8HiY9AOrJUN1ZBNlBGOEFUV0hYUVA4T0xJUDlVRExFUi4u&amp;route=shorturl">CCP-AHC Expression of Interest for Research Software - Codes, Pipelines, and Workflows</a></li>
</ul></li>
</ul></li>
</ul>
</section>
</section>
<section id="lightning-talks" class="level1">
<h1>Lightning talks</h1>
<section id="overview" class="level2">
<h2 class="anchored" data-anchor-id="overview">Overview</h2>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/HPC/Durham-HPC-Days/2025-sponsors.webp" class="img-fluid figure-img"></p>
<figcaption>Sponsors</figcaption>
</figure>
</div>
</section>
<section id="vast" class="level2">
<h2 class="anchored" data-anchor-id="vast">VAST?</h2>
<p>One of the presenter from a vendor (spondor) discussed how they use Bε-trees in their storage solution and why it is superior, including “every write is a snapshot”.</p>
<p>It felt very much a sales pitch and it sounds too good to be true (that it doesn’t have compromise in other factors)</p>
</section>
<section id="cornelis-networks" class="level2">
<h2 class="anchored" data-anchor-id="cornelis-networks">Cornelis networks</h2>
<p>Presented a new Omni-Path product at 400Gbps with superior metrics comparing to competitor InfiniBand. More details will be presented at ISC2025.</p>
</section>
<section id="cambridge-rcs" class="level2">
<h2 class="anchored" data-anchor-id="cambridge-rcs">Cambridge RCS</h2>
<p>Not from the lightning talk, but the DAWN supercomputer at Cambridge RCS is a gift from Intel and Dell, alledgedly because they have built such a good collaborative relationship with the vendors.</p>
</section>
</section>
<section id="tutorial" class="level1">
<h1>Tutorial</h1>
<section id="tutorial-amd-gpus-simplify-your-hpc-application-port-to-gpus---openmp-and-managed-memory-on-amd-mi300a-and-mi300x" class="level2">
<h2 class="anchored" data-anchor-id="tutorial-amd-gpus-simplify-your-hpc-application-port-to-gpus---openmp-and-managed-memory-on-amd-mi300a-and-mi300x">Tutorial: AMD GPUs: Simplify your HPC Application Port to GPUs - OpenMP and Managed Memory on AMD MI300A and MI300X</h2>
<p>Presenter: Bob Robey (AMD)</p>
<p>Fail to demonstrate how their compilers utilize the unified memory in their hardware, and in general a very poor tutorial.</p>
</section>
<section id="references" class="level2">




</section>
</section>

<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-laukemann_cloverleaf_2024" class="csl-entry">
Laukemann, Jan, Thomas Gruber, Georg Hager, Dossay Oryspayev, and Gerhard Wellein. 2024. <span>“CloverLeaf on Intel Multi-Core CPUs: A Case Study in Write-Allocate Evasion.”</span> <em>2024 IEEE International Parallel and Distributed Processing Symposium (IPDPS)</em>, May 27, 350–60. <a href="https://doi.org/10.1109/IPDPS57955.2024.00038">https://doi.org/10.1109/IPDPS57955.2024.00038</a>.
</div>
<div id="ref-espinosa_detrimental_2024" class="csl-entry">
Tuft, Adam S., Tobias Weinzierl, and Michael Klemm. 2024. <span>“Detrimental Task Execution Patterns in Mainstream OpenMP® Runtimes.”</span> In <em>Advancing OpenMP for Future Accelerators</em>, edited by Alexis Espinosa, Michael Klemm, Bronis R. De Supinski, Maciej Cytowski, and Jannis Klinkenberg, vol. 15195. Lecture Notes in Computer Science. Springer Nature Switzerland. <a href="https://doi.org/10.1007/978-3-031-72567-8_14">https://doi.org/10.1007/978-3-031-72567-8_14</a>.
</div>
</div></section></div> ]]></description>
  <category>Internal presentation</category>
  <category>Durham HPC Days</category>
  <guid>https://blog.kolen.dev/HPC/Durham-HPC-Days/2025-article.html</guid>
  <pubDate>Sat, 07 Jun 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Scientific Computing with JAX</title>
  <dc:creator>Kolen Cheung</dc:creator>
  <link>https://blog.kolen.dev/RSE/PyAutoLens/2025-06-04-autojax-article.html</link>
  <description><![CDATA[ 





<!-- Everything before the first heading is article-only. Keep this comment
INSIDE the div: a raw HTML block out here survives into slidy and pandoc turns
it into a stray empty first slide. -->
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><embed src="./2025-06-04-autojax.html" style="width:100.0%" height="400"></p>
<figcaption><a href="./2025-06-04-autojax.html">Slide to the talk</a></figcaption>
</figure>
</div>
<p>Below is a companion article to the Durham HPC Days 2025 talk on porting a gravitational lensing likelihood from Numba to JAX for JWST analysis. The slides and this article are generated from a single source, so everything that was on screen is here, together with an AI transcript of what was said around it.</p>
<p>Hello everyone. My name is Kolen Cheung, and I’m a Research Software Engineer at the University of Exeter. Today, I’m going to talk about a project I’ve worked on to adopt JAX for scientific computing, specifically a case study in evaluating gravitational lensing likelihood.</p>
<section id="motivations" class="level1">
<h1>Motivations</h1>
<section id="physics-revealing-the-nature-of-dark-matter-with-the-james-webb-space-telescope-jwst" class="level2">
<h2 class="anchored" data-anchor-id="physics-revealing-the-nature-of-dark-matter-with-the-james-webb-space-telescope-jwst">Physics: revealing the nature of dark matter with the James Webb Space Telescope (JWST)</h2>
<div class="notes">
<p>The project’s motivation is to probe the nature of dark matter with the <em>James Webb Space Telescope</em> (JWST). The science case is to investigate the low-mass region — below roughly <img src="https://latex.codecogs.com/png.latex?10%5E%7B8.5%7D%20M_%5Codot"> — of dark matter substructures in our universe. This is the region where the well-established ΛCDM model and alternative dark matter models make statistically different predictions.</p>
<p>Substructures in that mass range are generally invisible, so we cannot observe them directly: we have to probe them via strong gravitational lensing. This was first demonstrated two years ago in <span class="citation" data-cites="nightingale_scanning_2023">James W. Nightingale et al.<sup>1</sup></span> using <em>Hubble Space Telescope</em> (HST) data, so the natural next step is to take advantage of the 4+ wavebands and the high quality observations from JWST.</p>
</div>
</section>
<section id="how-pyautolens" class="level2">
<h2 class="anchored" data-anchor-id="how-pyautolens">How? PyAutoLens</h2>
<div class="notes">
<p>To do this, we use an existing package called <code>PyAutoLens</code>. It has two halves:</p>
</div>
<ol type="1">
<li><p>Mass modeling: parametric, non-linear, multi-phase models</p></li>
<li><p>Source reconstruction: linear, reconstructing unlensed light distribution via different kinds of meshes: rectangular, Delaunay, Voronoi</p></li>
</ol>
<p>Log-likelihood function takes the output of (1) and computes its likelihood (where (2) is part of the calculation).</p>
<p>Key goal is to automate the whole process and apply it to large datasets.</p>
</section>
<section id="the-power-of-likelihood-in-theory" class="level2">
<h2 class="anchored" data-anchor-id="the-power-of-likelihood-in-theory">The power of likelihood in theory</h2>
<div style="text-align:center">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/dot/likelihood.svg" class="img-fluid figure-img" style="width:67.0%"></p>
<figcaption><img src="https://latex.codecogs.com/png.latex?P(%5Cboldsymbol%7B%5CTheta%7D%20%7C%20%5Cmathbf%7BD%7D,%20M)%20=%0A%5Cfrac%7BP(%5Cmathbf%7BD%7D%20%7C%20%5Cboldsymbol%7B%5CTheta%7D,%20M)%0AP(%5Cboldsymbol%7B%5CTheta%7D%20%7C%20M)%7D%7BP(%5Cmathbf%7BD%7D%20%7C%20M)%7D%20%5Cequiv%0A%5Cfrac%7B%5Cmathcal%7BL%7D(%5Cboldsymbol%7B%5CTheta%7D)%20%5Cpi(%5Cboldsymbol%7B%5CTheta%7D)%7D%0A%7B%5Cmathcal%7BZ%7D%7D"></figcaption>
</figure>
</div>
</div>
<div class="notes">
<p>In cosmology, we cannot perform experiments; we only have one observable universe. All we can do is make observations and then use simulations to infer the parameters of physical models through statistical methods. That is the loop in the diagram above: physics gives us parametric models, models give us predictions, the cosmos gives us observables that our pipelines compress into data, and a sampler ties the two branches together through the likelihood function.</p>
</div>
</section>
<section id="the-power-of-likelihood-in-action" class="level2">
<h2 class="anchored" data-anchor-id="the-power-of-likelihood-in-action">The power of likelihood in action</h2>
<!-- The deck lays these out in columns; the article wants captioned figures
     and a subfigure group that survives LaTeX. Same images, two presentations. -->
<ul>
<li>25 free parameters
<ul>
<li>Lens Light (11): Sersic + Exponential</li>
<li>Lens Mass (7): SIE + Shear</li>
<li>Source Light (7): Sersic</li>
</ul></li>
</ul>
<p>PyAutoLens (via PyAutoFit) supports Nested sampling (<strong>Dynesty</strong>), MCMC (emcee), particle swarm optimization (PySwarms)</p>
<div id="fig-lens" class="quarto-layout-panel">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-lens-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/image7.png" class="img-fluid figure-img"></p>
<figcaption>The lens light.</figcaption>
</figure>
</div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/image3.png" class="img-fluid figure-img"></p>
<figcaption>The observation: lensed source arcs, inside the analysis mask.</figcaption>
</figure>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-lens-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: What the 25-parameter model has to reproduce.
</figcaption>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/image25.gif" class="img-fluid figure-img" style="width:67.0%"></p>
<figcaption>As the model iterates, the chi-squared value decreases, and the predicted lens image more closely matches the observation — successfully estimating the mass model.</figcaption>
</figure>
</div>
<div class="notes">
<p><code>PyAutoLens</code> uses a sampler to iterate and find the best solution. The goal is to find the mass profile that produces the observed image.</p>
</div>
</section>
<section id="computational-challenges" class="level2">
<h2 class="anchored" data-anchor-id="computational-challenges">Computational challenges</h2>
<div class="notes">
<p>However, there are computational challenges.</p>
</div>
<ul>
<li>PyAutoLens originally is implemented in Numba</li>
<li>For single-band HST data, the image processing analysis of a single lens takes approximately 48 hours over 76 CPUs.
<ul>
<li>image pixels: <img src="https://latex.codecogs.com/png.latex?M%20%5Csim%2010,000"></li>
<li>source image pixels: <img src="https://latex.codecogs.com/png.latex?S%20%5Csim%202,000"></li>
<li><img src="https://latex.codecogs.com/png.latex?3%5Ctext%7B%20s%7D"> for 1 iteration, <img src="https://latex.codecogs.com/png.latex?O(100,000)"> iterations needed.</li>
</ul></li>
<li>For JWST’s 4x freq. bands, <img src="https://latex.codecogs.com/png.latex?M%20%5Crightarrow%204M,%20S%20%5Crightarrow%204S">. Runtime: <img src="https://latex.codecogs.com/png.latex?%5Csim%203%5Ctext%7B%20s%7D%20%5Crightarrow%2060%5Ctext%7B%20s%7D">. I.e. <img src="https://latex.codecogs.com/png.latex?%5Csim%20%5Ctimes%2020"></li>
</ul>
<div class="notes">
<p>That extra cost buys us something, though: because JWST provides data across multiple frequency bands, we get colour information that helps guide the model to better solutions.</p>
</div>
</section>
<section id="the-cowls-sample" class="level2">
<h2 class="anchored" data-anchor-id="the-cowls-sample">The COWLS sample</h2>
<div style="text-align:center">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/mahlerCOSMOSWebLensSurvey2025-fig1.png" class="img-fluid figure-img" style="width:67.0%"></p>
<figcaption>The 17 most spectacular lenses from the COWLS sample, revealed by the JWST imaging through our visual inspection of the COSMOS-Web field. The images are produced combining the four filters (F115W, F150W, F277W, F444W) for an ideal rendering of the lensing evidence.<span class="citation" data-cites="mahler_cosmos-web_2025"><sup>2</sup></span></figcaption>
</figure>
</div>
</div>
</section>
<section id="why-jax" class="level2">
<h2 class="anchored" data-anchor-id="why-jax">Why JAX?</h2>
<div class="notes">
<p>The question is: why port <code>PyAutoLens</code> to JAX for calculating the log-likelihood function?</p>
</div>
<ul>
<li>Herculens,<span class="citation" data-cites="galan_using_2022"><sup>3</sup></span> GIGA-Lens<span class="citation" data-cites="gu_giga-lens_2022"><sup>4</sup></span> demonstrated successful adoption of JAX in modeling strong gravitational lensing.</li>
<li>Performance gains potentially come from:
<ul>
<li>Functions implemented with JAX become faster</li>
<li>Running on accelerators</li>
<li>Gradient information reduces no. of iterations in fitting</li>
</ul></li>
<li>This project ports the likelihood function, a subset of functionality provided in PyAutoLens, from Numba to JAX</li>
</ul>
</section>
</section>
<section id="lesson-learnt-programming-experience" class="level1">
<h1>Lesson learnt (programming experience)</h1>
<section id="methodology" class="level2">
<h2 class="anchored" data-anchor-id="methodology">Methodology</h2>
<ul>
<li>Porting
<ol type="1">
<li>translate the function to Math</li>
<li>translate the Math to Numba, using vectorized programming as much as possible to anticipate the programming paradigm in JAX</li>
<li>translate the Numba function to JAX, where in simple case would just work</li>
<li>further optimize from there</li>
</ol></li>
<li>Organizing &amp; Testing
<ul>
<li>Functions are then kept in 3 different modules, <code>original</code> for the original functions, <code>numba</code> for those ported in (2), <code>jax</code> for those ported in (3)</li>
<li>Metaprogramming is used in setting up unit-test framework (via pytest) to guarantee correctness. pytest-benchmark is used to compare performance differences between these implementations. This feeds back into step (4).</li>
</ul></li>
</ul>
</section>
<section id="what-is-numba" class="level2">
<h2 class="anchored" data-anchor-id="what-is-numba">What is Numba</h2>
<ul>
<li>Numba is a jit compiler supporting a subset of Python and NumPy operations, powered by LLVM. While it is possible to target the GPU via CUDA, it requires rewriting the function using different APIs and paradigms, not to mention it is CUDA (i.e.&nbsp;NVidia) only.</li>
</ul>
</section>
<section id="what-is-jax" class="level2">
<h2 class="anchored" data-anchor-id="what-is-jax">What is JAX</h2>
<ul>
<li>JAX is a jit compiler, tracing compiler by Google, powered by XLA compiler, originated from Google. JAX is designed primarily for machine learning workloads but is suitable for scientific computing as well. It is a tracing compiler removing side-effects of function. I.e. effectively it encourages functional programming paradigm and thinking. It automatically targets multiple hardware architectures including CPU, GPU, TPU, without requiring rewriting.</li>
<li>I.e. it solves the “two-language problem”, or more accurately, “three-implementation problem”: prototype/API, CPU, GPU.</li>
</ul>
</section>
<section id="numba-vs.-jax" class="level2">
<h2 class="anchored" data-anchor-id="numba-vs.-jax">Numba vs.&nbsp;JAX</h2>
<ul>
<li>Numba and JAX are both Domain-Specific Languages (DSLs), with different kinds of fallbacks when complete jit compilation of a function is not possible.
<ul>
<li>Better think of it as language + compiler + library.</li>
</ul></li>
</ul>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="https://blog.kolen.dev/media/PyAutoLens/numba-vs-jax.svg" class="img-fluid figure-img" style="width:60.0%"></p>
<figcaption>Both Numba and JAX are DSLs embedded in Python, with the foreign function interface as the shared escape hatch.</figcaption>
</figure>
</div>
</section>
<section id="numba-vs.-jax-side-by-side" class="level2">
<h2 class="anchored" data-anchor-id="numba-vs.-jax-side-by-side">Numba vs.&nbsp;JAX, side by side</h2>
<table class="caption-top table">
<caption>Numba vs.&nbsp;JAX</caption>
<thead>
<tr class="header">
<th>Numba</th>
<th>JAX</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>C-like mini language</td>
<td>Smaller language (<img src="https://latex.codecogs.com/png.latex?%5Ctext%7BJAX%7D%20%5Cunderset%7B%5Csim%7D%7B%5Csubset%7D%20%5Ctext%7BNumba%7D">): restrictions on control flow, mutation, and dynamic shapes</td>
</tr>
<tr class="even">
<td>Implements a subset of Python+NumPy, with a parallelization model similar to a mini-“OpenMP”</td>
<td>Implements a subset of Python+NumPy+SciPy exposed via duck-typing.</td>
</tr>
<tr class="odd">
<td>NumPy implementations are dropped in replacement but only a subset is implemented. Calling NumPy within jitted function is completely hijacked. <a href="https://numba.readthedocs.io/en/stable/reference/numpysupported.html">Documentation is minimal.</a></td>
<td><a href="https://docs.jax.dev/en/latest/jax.numpy.html"><code>jax.numpy</code></a> and <a href="https://docs.jax.dev/en/latest/jax.scipy.html"><code>jax.scipy</code></a> have similar API comparing to NumPy and SciPy, but has its own documentation. This facilitates <a href="https://docs.jax.dev/en/latest/notebooks/Common_Gotchas_in_JAX.html">deviations in behaviors</a>.</td>
</tr>
<tr class="even">
<td>Functions “recompile” whenever input type changes.</td>
<td>Functions “recompile” whenever input type <strong>and shape</strong> changes.</td>
</tr>
<tr class="odd">
<td>No automatic compiling &amp; offloading to accelerator. No autograd/autodiff.</td>
<td>Going through FFI is more costly: memory transfer from and to device, losing autograd/autodiff.</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>The documentation point matters more than it looks. Numba’s documentation on its internal workings can be minimal, whereas <code>jax.numpy</code> and <code>jax.scipy</code> have their own comprehensive documentation — which is what makes it practical to deviate from the reference implementation when you need to.</p>
</div>
</section>
<section id="characteristics-of-jax" class="level2">
<h2 class="anchored" data-anchor-id="characteristics-of-jax">Characteristics of JAX</h2>
<ul>
<li><p>tracing compiler &amp; recompile per shape change <img src="https://latex.codecogs.com/png.latex?%5CRightarrow"> <code>static_argnums</code></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb1-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@partial</span>(jax.jit, static_argnums<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>)</span>
<span id="cb1-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> this_recompile_everytime(shape):</span>
<span id="cb1-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> jax.numpy.zeros(shape)</span></code></pre></div></div></li>
<li><p>Compiler Driven Design</p>
<ul>
<li>Especially in JAX, partly because of its functional paradigm, framing your problem in JAX idiomatic expressions results in great speed up, sometimes more than you could do otherwise in Numba because of its design (recompile per shape, fusion/fusing compatible operations, etc.), but you’ll hit a wall if you want more low-level optimizations.</li>
<li>It can also means performance improvements can come for free through compiler improvements, as long as your code is written in JAX idiomatic way.</li>
</ul></li>
<li><p>Easy to port to GPU without setting one up.</p></li>
<li><p>JAX vs numba-cuda: The XLA compiler handles device-specific optimization automatically.</p></li>
<li><p>JAX nudges you to write correct code, and performance comes as a bonus.</p></li>
</ul>
</section>
</section>
<section id="benchmark-analysis" class="level1">
<h1>Benchmark analysis</h1>
<section id="tildewcode-original-version" class="level2">
<h2 class="anchored" data-anchor-id="tildewcode-original-version"><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">—Code: original version</h2>
<div class="notes">
<p>Let’s look at some benchmarks. The function under study here is <img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">, a weight matrix used in the interferometer likelihood:</p>
</div>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7BW%7D_%7Bij%7D%20=%20%5Csum_%7Bk=1%7D%5EK%20%5Cfrac%7B1%7D%7Bn_k%5E2%7D%20%5Ccos(2%5Cpi%5B(g_%7Bi1%7D%20-%20g_%7Bj1%7D)u_%7Bk0%7D%20+%20(g_%7Bi0%7D%20-%20g_%7Bj0%7D)u_%7Bk1%7D%5D)"></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb2-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@numba.jit</span>(nopython<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, nogil<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, parallel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb2-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_tilde_curvature_interferometer_from(</span>
<span id="cb2-3">    noise_map_real: np.ndarray,</span>
<span id="cb2-4">    uv_wavelengths: np.ndarray,</span>
<span id="cb2-5">    grid_radians_slim: np.ndarray,</span>
<span id="cb2-6">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray:</span>
<span id="cb2-7">    w_tilde <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((grid_radians_slim.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>], grid_radians_slim.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]))</span>
<span id="cb2-8"></span>
<span id="cb2-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(w_tilde.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]):</span>
<span id="cb2-10">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> j <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(i, w_tilde.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]):</span>
<span id="cb2-11">            y_offset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> grid_radians_slim[i, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> grid_radians_slim[j, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb2-12">            x_offset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> grid_radians_slim[i, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> grid_radians_slim[j, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb2-13"></span>
<span id="cb2-14">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> vis_1d_index <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(uv_wavelengths.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]):</span>
<span id="cb2-15">                w_tilde[i, j] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> noise_map_real[vis_1d_index] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">**</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.cos(</span>
<span id="cb2-16">                    <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span></span>
<span id="cb2-17">                    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.pi</span>
<span id="cb2-18">                    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> (y_offset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> uv_wavelengths[vis_1d_index, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> x_offset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> uv_wavelengths[vis_1d_index, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>])</span>
<span id="cb2-19">                )</span>
<span id="cb2-20"></span>
<span id="cb2-21">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> i <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(w_tilde.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]):</span>
<span id="cb2-22">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> j <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">range</span>(i, w_tilde.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]):</span>
<span id="cb2-23">            w_tilde[j, i] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> w_tilde[i, j]</span>
<span id="cb2-24"></span>
<span id="cb2-25">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> w_tilde</span></code></pre></div></div>
</section>
<section id="tildewcode-1st-try" class="level2">
<h2 class="anchored" data-anchor-id="tildewcode-1st-try"><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">—Code: 1st try</h2>
<div class="notes">
<p>Following the methodology, the first JAX attempt is a direct, fully vectorized transcription of the math:</p>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb3-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jax.jit</span></span>
<span id="cb3-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_tilde_curvature_interferometer_from(</span>
<span id="cb3-3">    noise_map_real: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb3-4">    uv_wavelengths: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb3-5">    grid_radians_slim: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb3-6">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb3-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (M, M, 1, 2)</span></span>
<span id="cb3-8">    g_ij <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>  grid_radians_slim.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> grid_radians_slim.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb3-9">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 1, K, 2)</span></span>
<span id="cb3-10">    u_k <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> uv_wavelengths.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb3-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (</span>
<span id="cb3-12">        jnp.cos(</span>
<span id="cb3-13">            (<span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> jnp.pi) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span></span>
<span id="cb3-14">            <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (M, M, K)</span></span>
<span id="cb3-15">            (</span>
<span id="cb3-16">                g_ij[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_k[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb3-17">                g_ij[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> u_k[:, :, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb3-18">            )</span>
<span id="cb3-19">        ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span></span>
<span id="cb3-20">        <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># (1, 1, K)</span></span>
<span id="cb3-21">        jnp.square(noise_map_real).reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>)</span>
<span id="cb3-22">    ).<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">sum</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># sum over k</span></span></code></pre></div></div>
</section>
<section id="tildewcode-2nd-try" class="level2">
<h2 class="anchored" data-anchor-id="tildewcode-2nd-try"><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">—Code: 2nd try</h2>
<div class="notes">
<p>Expanding the cosine of a difference into a product of cosines and sines turns the whole thing into two matrix multiplications:</p>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jax.jit</span></span>
<span id="cb4-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_tilde_curvature_interferometer_from(</span>
<span id="cb4-3">    noise_map_real: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb4-4">    uv_wavelengths: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb4-5">    grid_radians_slim: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb4-6">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb4-7">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># A_mk, m&lt;M, k&lt;K</span></span>
<span id="cb4-8">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># assume M &gt; K to put TWO_PI multiplication there</span></span>
<span id="cb4-9">    A <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> grid_radians_slim <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> (TWO_PI <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> uv_wavelengths)[:, ::<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>].T</span>
<span id="cb4-10"></span>
<span id="cb4-11">    noise_map_real_inv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> jnp.reciprocal(noise_map_real)</span>
<span id="cb4-12">    C <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> jnp.cos(A) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> noise_map_real_inv</span>
<span id="cb4-13">    S <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> jnp.sin(A) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> noise_map_real_inv</span>
<span id="cb4-14"></span>
<span id="cb4-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> C <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> C.T <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> S <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@</span> S.T</span></code></pre></div></div>
</section>
<section id="tildewcode-digression-in-problem-sizes" class="level2">
<h2 class="anchored" data-anchor-id="tildewcode-digression-in-problem-sizes"><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">—Code: digression in problem sizes</h2>
<dl>
<dt>Number of image pixels</dt>
<dd>
<img src="https://latex.codecogs.com/png.latex?M%20%5Csim%2070,000%20%5CRightarrow%20M%5E2%20%5Csim%205%20%5Ctimes%2010%5E9,%20%5Cquad%200%20%5Cleq%20i,%20j%20%3C%20M">
</dd>
<dd>
<img src="https://latex.codecogs.com/png.latex?N%20%5Csim%20%5Csqrt%7BM%7D%20%5Csim%20300">
</dd>
<dt>Number of visibilities</dt>
<dd>
<img src="https://latex.codecogs.com/png.latex?K%20%5Csim%2010%5E7,%20%5Cquad%200%20%5Cleq%20k%20%3C%20K">
</dd>
</dl>
<p><img src="https://latex.codecogs.com/png.latex?(M,%20M,%20K,%202)"> of 64-bit array would be <img src="https://latex.codecogs.com/png.latex?%5Csim%20700"> PiB!</p>
<p>While <img src="https://latex.codecogs.com/png.latex?(M,%20M)"> of 64-bit array would be <img src="https://latex.codecogs.com/png.latex?%5Csim%2040"> GiB only.</p>
<div class="notes">
<p>In other words, the naive vectorized implementation from the 1st try is computationally infeasible — not because the answer is big, but because the intermediate is. The final result is manageable, so the problem is tractable as long as you avoid ever expanding the largest dimension in memory.</p>
</div>
</section>
<section id="tildewcode-final-trynumba" class="level2">
<h2 class="anchored" data-anchor-id="tildewcode-final-trynumba"><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">—Code: final try—Numba</h2>
<div class="notes">
<p>In Numba, the answer is to loop over <img src="https://latex.codecogs.com/png.latex?K"> and use <code>prange</code> for a parallel reduction, much like OpenMP:</p>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@numba.jit</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"f8[:, ::1](f8[::1], f8[:, ::1], f8[:, ::1])"</span>, nopython<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, nogil<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>, parallel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">True</span>)</span>
<span id="cb5-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_tilde_curvature_interferometer_from(</span>
<span id="cb5-3">    noise_map_real: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb5-4">    uv_wavelengths: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb5-5">    grid_radians_slim: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb5-6">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb5-7">    M <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> grid_radians_slim.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb5-8">    K <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> uv_wavelengths.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb5-9">    g_2pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TWO_PI <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> grid_radians_slim</span>
<span id="cb5-10">    δg_2pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> g_2pi.reshape(<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> g_2pi.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb5-11"></span>
<span id="cb5-12">    w <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> np.zeros((M, M))</span>
<span id="cb5-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> k <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> numba.prange(K):</span>
<span id="cb5-14">        w <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+=</span> np.cos(δg_2pi[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> uv_wavelengths[k, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> δg_2pi[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> uv_wavelengths[k, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> np.reciprocal(</span>
<span id="cb5-15">            np.square(noise_map_real[k])</span>
<span id="cb5-16">        )</span>
<span id="cb5-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> w</span></code></pre></div></div>
</section>
<section id="tildewcode-final-tryjax" class="level2">
<h2 class="anchored" data-anchor-id="tildewcode-final-tryjax"><img src="https://latex.codecogs.com/png.latex?%5Ctilde%7Bw%7D">—Code: final try—JAX</h2>
<div class="notes">
<p>JAX has no <code>prange</code>, and its arrays are immutable, so an in-place reduction is not available. The idiomatic solution is <code>jax.lax.scan</code>, building up the sum iteratively:</p>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode py code-with-copy"><code class="sourceCode python"><span id="cb6-1"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">@jax.jit</span></span>
<span id="cb6-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> w_tilde_curvature_interferometer_from(</span>
<span id="cb6-3">    noise_map_real: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb6-4">    uv_wavelengths: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb6-5">    grid_radians_slim: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb6-6">) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb6-7">    M <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> grid_radians_slim.shape[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb6-8">    g_2pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> TWO_PI <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> grid_radians_slim</span>
<span id="cb6-9">    δg_2pi <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> g_2pi.reshape(M, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> g_2pi.reshape(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>, M, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="cb6-10">    δg_2pi_y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> δg_2pi[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>]</span>
<span id="cb6-11">    δg_2pi_x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> δg_2pi[:, :, <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]</span>
<span id="cb6-12"></span>
<span id="cb6-13">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> f_k(</span>
<span id="cb6-14">        noise_map_real: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>,</span>
<span id="cb6-15">        uv_wavelengths: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb6-16">    ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]:</span>
<span id="cb6-17">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> jnp.cos(δg_2pi_x <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> uv_wavelengths[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> δg_2pi_y <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> uv_wavelengths[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>]) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> jnp.reciprocal(</span>
<span id="cb6-18">            jnp.square(noise_map_real)</span>
<span id="cb6-19">        )</span>
<span id="cb6-20"></span>
<span id="cb6-21">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> f_scan(</span>
<span id="cb6-22">        sum_: np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64],</span>
<span id="cb6-23">        args: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span>, np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64]],</span>
<span id="cb6-24">    ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[np.ndarray[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">tuple</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>, <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>], np.float64], <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>]:</span>
<span id="cb6-25">        noise_map_real, uv_wavelengths <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> args</span>
<span id="cb6-26">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> sum_ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> f_k(noise_map_real, uv_wavelengths), <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span></span>
<span id="cb6-27"></span>
<span id="cb6-28">    res, _ <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> jax.lax.scan(</span>
<span id="cb6-29">        f_scan,</span>
<span id="cb6-30">        jnp.zeros((M, M)),</span>
<span id="cb6-31">        (</span>
<span id="cb6-32">            noise_map_real,</span>
<span id="cb6-33">            uv_wavelengths,</span>
<span id="cb6-34">        ),</span>
<span id="cb6-35">    )</span>
<span id="cb6-36">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> res</span></code></pre></div></div>
</section>
<section id="match-1-numba-vs-jax-with-1-cpu-core" class="level2">
<h2 class="anchored" data-anchor-id="match-1-numba-vs-jax-with-1-cpu-core">Match 1: Numba vs JAX with 1 CPU core</h2>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=64,%5Cquad%20B=3,%5Cquad%20K=32768,%5Cquad%20P=32,%5Cquad%20S=256">, <code>w_tilde_curvature_interferometer_from</code></caption>
<thead>
<tr class="header">
<th>Implementation</th>
<th>s</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>jax_compact_expanded</code></td>
<td>2.5535 (1.0)</td>
<td>0.0032</td>
</tr>
<tr class="even">
<td><code>jax_compact</code></td>
<td>2.5543 (1.00)</td>
<td>0.0050</td>
</tr>
<tr class="odd">
<td><code>numba_compact</code></td>
<td>2.8768 (1.13)</td>
<td>0.0012</td>
</tr>
<tr class="even">
<td><code>numba_compact_expanded</code></td>
<td>2.8967 (1.13)</td>
<td>0.0004</td>
</tr>
<tr class="odd">
<td><code>original_preload</code></td>
<td>11.0392 (4.32)</td>
<td>0.0010</td>
</tr>
<tr class="even">
<td><code>original_preload_expanded</code></td>
<td>11.0686 (4.33)</td>
<td>0.0005</td>
</tr>
<tr class="odd">
<td><code>jax</code></td>
<td>3,368.6229 (&gt;1000.0)</td>
<td>1.6803</td>
</tr>
<tr class="even">
<td><code>original</code></td>
<td>3,561.0805 (&gt;1000.0)</td>
<td>0.2255</td>
</tr>
<tr class="odd">
<td><code>numba</code></td>
<td>3,702.7006 (&gt;1000.0)</td>
<td>0.7385</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>On a single CPU core, the JAX implementation is slightly faster than the Numba version — about 13%. But look at what changing the underlying algorithm buys you: the compact formulation is a factor of four faster than the best implementation of the original algorithm, and over a thousand times faster than the naive ports of it, all without leaving Numba. Algorithmic improvements can be far more impactful than framework changes.</p>
</div>
<!--
```sh
pixi run pytest-benchmark compare 1_N=64_B=3_K=32768_P=32_S=256_NUM_THREADS=1 --columns=mean,stddev,ops,rounds,iterations --sort=mean
```

```log
--------------------------------------------- benchmark 'w_tilde_curvature_interferometer_from_DataGenerated': 9 tests ---------------------------------------------
Name (time in s)                                                                              Mean            StdDev               OPS            Rounds  Iterations
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_w_tilde_curvature_interferometer_from_jax_compact_expanded[DataGenerated]              2.5535 (1.0)      0.0032 (7.49)     0.3916 (1.0)           5           1
test_w_tilde_curvature_interferometer_from_jax_compact[DataGenerated]                       2.5543 (1.00)     0.0050 (11.60)    0.3915 (1.00)          5           1
test_w_tilde_curvature_interferometer_from_numba_compact[DataGenerated]                     2.8768 (1.13)     0.0012 (2.73)     0.3476 (0.89)          5           1
test_w_tilde_curvature_interferometer_from_numba_compact_expanded[DataGenerated]            2.8967 (1.13)     0.0004 (1.0)      0.3452 (0.88)          5           1
test_w_tilde_curvature_interferometer_original_preload[DataGenerated]                      11.0392 (4.32)     0.0010 (2.28)     0.0906 (0.23)          5           1
test_w_tilde_curvature_interferometer_from_original_preload_expanded[DataGenerated]        11.0686 (4.33)     0.0005 (1.21)     0.0903 (0.23)          5           1
test_w_tilde_curvature_interferometer_from_jax[DataGenerated]                           3,368.6229 (>1000.0)  1.6803 (>1000.0)  0.0003 (0.00)          5           1
test_w_tilde_curvature_interferometer_from_original[DataGenerated]                      3,561.0805 (>1000.0)  0.2255 (519.83)   0.0003 (0.00)          5           1
test_w_tilde_curvature_interferometer_from_numba[DataGenerated]                         3,702.7006 (>1000.0)  0.7385 (>1000.0)  0.0003 (0.00)          5           1
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
```
-->
</section>
<section id="match-2-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100" class="level2">
<h2 class="anchored" data-anchor-id="match-2-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100">Match 2: Numba with 128 CPU cores and JAX with CUDA on GPU (A100)</h2>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=32,%5Cquad%20B=300,%5Cquad%20K=8192,%5Cquad%20P=32,%5Cquad%20S=256">, <code>w_tilde_curvature_interferometer_from</code></caption>
<thead>
<tr class="header">
<th>Implementation</th>
<th>ms</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>numba_compact</code></td>
<td>2.5029 (1.0)</td>
<td>0.0520</td>
</tr>
<tr class="even">
<td><code>numba_compact_expanded</code></td>
<td>3.7808 (1.51)</td>
<td>0.0415</td>
</tr>
<tr class="odd">
<td><code>jax_compact_expanded</code></td>
<td>58.6799 (23.44)</td>
<td>9.1624</td>
</tr>
<tr class="even">
<td><code>jax_compact</code></td>
<td>61.5560 (24.59)</td>
<td>6.8555</td>
</tr>
<tr class="odd">
<td><code>jax</code></td>
<td>143.2451 (57.23)</td>
<td>0.0749</td>
</tr>
<tr class="even">
<td><code>original_preload</code></td>
<td>840.0727 (335.63)</td>
<td>0.1761</td>
</tr>
<tr class="odd">
<td><code>original_preload_expanded</code></td>
<td>842.6588 (336.67)</td>
<td>0.4933</td>
</tr>
<tr class="even">
<td><code>numba</code></td>
<td>1,794.1949 (716.83)</td>
<td>14.9648</td>
</tr>
<tr class="odd">
<td><code>original</code></td>
<td>69,304.2738 (&gt;1000.0)</td>
<td>13.5543</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>For the original algorithm, JAX on the GPU is significantly faster than Numba on 128 cores — as you’d expect. But for the alternative, compact algorithm, JAX was <em>slower</em>, likely because the problem size was not large enough to saturate the GPU.</p>
</div>
<!--
```sh
pixi run pytest-benchmark compare --columns=mean,stddev,ops,rounds,iterations --sort=mean 0009_N=32_B=300_K=8192_P=32_S=256_NUM_THREADS=128_cuda
```

```log
----------------------------------------------- benchmark 'w_tilde_curvature_interferometer_from_DataGenerated': 9 tests -----------------------------------------------
Name (time in ms)                                                                              Mean             StdDev                 OPS            Rounds  Iterations
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_w_tilde_curvature_interferometer_from_numba_compact[DataGenerated]                      2.5029 (1.0)       0.0520 (1.25)     399.5298 (1.0)         260           1
test_w_tilde_curvature_interferometer_from_numba_compact_expanded[DataGenerated]             3.7808 (1.51)      0.0415 (1.0)      264.4928 (0.66)        196           1
test_w_tilde_curvature_interferometer_from_jax_compact_expanded[DataGenerated]              58.6799 (23.44)     9.1624 (220.76)    17.0416 (0.04)         12           1
test_w_tilde_curvature_interferometer_from_jax_compact[DataGenerated]                       61.5560 (24.59)     6.8555 (165.18)    16.2454 (0.04)         12           1
test_w_tilde_curvature_interferometer_from_jax[DataGenerated]                              143.2451 (57.23)     0.0749 (1.80)       6.9810 (0.02)          5           1
test_w_tilde_curvature_interferometer_from_original_preload[DataGenerated]                 840.0727 (335.63)    0.1761 (4.24)       1.1904 (0.00)          5           1
test_w_tilde_curvature_interferometer_from_original_preload_expanded[DataGenerated]        842.6588 (336.67)    0.4933 (11.89)      1.1867 (0.00)          5           1
test_w_tilde_curvature_interferometer_from_numba[DataGenerated]                          1,794.1949 (716.83)   14.9648 (360.56)     0.5574 (0.00)          5           1
test_w_tilde_curvature_interferometer_from_original[DataGenerated]                      69,304.2738 (>1000.0)  13.5543 (326.58)     0.0144 (0.00)          5           1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
```
-->
</section>
<section id="bonus-round-1-numba-vs-jax-with-1-cpu-core-f" class="level2">
<h2 class="anchored" data-anchor-id="bonus-round-1-numba-vs-jax-with-1-cpu-core-f">Bonus round 1: Numba vs JAX with 1 CPU core (<img src="https://latex.codecogs.com/png.latex?F">)</h2>
<p><img src="https://latex.codecogs.com/png.latex?F%20=%20T%5ET%20%5Ctilde%7Bw%7D%20T"></p>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=64,%5Cquad%20B=3,%5Cquad%20K=32768,%5Cquad%20P=32,%5Cquad%20S=256">, <code>curvature_matrix</code></caption>
<thead>
<tr class="header">
<th>Implementation</th>
<th>ms</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>numba_sparse</code></td>
<td>8.0733 (1.0)</td>
<td>0.0501</td>
</tr>
<tr class="even">
<td><code>jax</code></td>
<td>19.7302 (2.44)</td>
<td>1.6986</td>
</tr>
<tr class="odd">
<td><code>jax_sparse</code></td>
<td>25.0091 (3.10)</td>
<td>0.1484</td>
</tr>
<tr class="even">
<td><code>jax_BCOO</code></td>
<td>48.5340 (6.01)</td>
<td>0.1571</td>
</tr>
<tr class="odd">
<td><code>numba_compact_sparse</code></td>
<td>49.8400 (6.17)</td>
<td>0.0794</td>
</tr>
<tr class="even">
<td><code>original_preload_direct</code></td>
<td>99.2061 (12.29)</td>
<td>0.3163</td>
</tr>
<tr class="odd">
<td><code>numba</code></td>
<td>125.3019 (15.52)</td>
<td>0.1143</td>
</tr>
<tr class="even">
<td><code>original</code></td>
<td>132.4863 (16.41)</td>
<td>0.1376</td>
</tr>
<tr class="odd">
<td><code>numba_compact_sparse_direct</code></td>
<td>139.9244 (17.33)</td>
<td>0.1562</td>
</tr>
<tr class="even">
<td><code>jax_compact_sparse_BCOO</code></td>
<td>379.7214 (47.03)</td>
<td>1.4144</td>
</tr>
<tr class="odd">
<td><code>jax_compact_sparse</code></td>
<td>380.8865 (47.18)</td>
<td>2.4322</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>On a single CPU core, the fastest algorithm uses sparse matrix operations in Numba.</p>
</div>
<!--
```sh
pixi run pytest-benchmark compare 1_N=64_B=3_K=32768_P=32_S=256_NUM_THREADS=1 --columns=mean,stddev,ops,rounds,iterations --sort=mean
```

```log
---------------------------------------------- benchmark 'curvature_matrix_DataGenerated': 11 tests ---------------------------------------------
Name (time in ms)                                                        Mean            StdDev                 OPS            Rounds  Iterations
-------------------------------------------------------------------------------------------------------------------------------------------------
test_curvature_matrix_numba_sparse[DataGenerated]                      8.0733 (1.0)      0.0501 (1.0)      123.8657 (1.0)         120           1
test_curvature_matrix_jax[DataGenerated]                              19.7302 (2.44)     1.6986 (33.89)     50.6837 (0.41)         30           1
test_curvature_matrix_jax_sparse[DataGenerated]                       25.0091 (3.10)     0.1484 (2.96)      39.9855 (0.32)         10           1
test_curvature_matrix_jax_BCOO[DataGenerated]                         48.5340 (6.01)     0.1571 (3.13)      20.6041 (0.17)          8           1
test_curvature_matrix_numba_compact_sparse[DataGenerated]             49.8400 (6.17)     0.0794 (1.58)      20.0642 (0.16)         21           1
test_curvature_matrix_original_preload_direct[DataGenerated]          99.2061 (12.29)    0.3163 (6.31)      10.0800 (0.08)         11           1
test_curvature_matrix_numba[DataGenerated]                           125.3019 (15.52)    0.1143 (2.28)       7.9807 (0.06)          8           1
test_curvature_matrix_original[DataGenerated]                        132.4863 (16.41)    0.1376 (2.75)       7.5479 (0.06)          8           1
test_curvature_matrix_numba_compact_sparse_direct[DataGenerated]     139.9244 (17.33)    0.1562 (3.12)       7.1467 (0.06)          8           1
test_curvature_matrix_jax_compact_sparse_BCOO[DataGenerated]         379.7214 (47.03)    1.4144 (28.22)      2.6335 (0.02)          5           1
test_curvature_matrix_jax_compact_sparse[DataGenerated]              380.8865 (47.18)    2.4322 (48.53)      2.6255 (0.02)          5           1
-------------------------------------------------------------------------------------------------------------------------------------------------
```
-->
</section>
<section id="bonus-round-2-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100-f" class="level2">
<h2 class="anchored" data-anchor-id="bonus-round-2-numba-with-128-cpu-cores-and-jax-with-cuda-on-gpu-a100-f">Bonus round 2: Numba with 128 CPU cores and JAX with CUDA on GPU (A100) (<img src="https://latex.codecogs.com/png.latex?F">)</h2>
<table class="caption-top table">
<caption><img src="https://latex.codecogs.com/png.latex?N=32,%5Cquad%20B=300,%5Cquad%20K=8192,%5Cquad%20P=32,%5Cquad%20S=256">, <code>curvature_matrix</code></caption>
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>Implementation</th>
<th>μs</th>
<th>σ</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>jax</code></td>
<td>260.5957 (1.0)</td>
<td>29.3714</td>
</tr>
<tr class="even">
<td><code>jax_BCOO</code></td>
<td>3,078.2068 (11.81)</td>
<td>35.9463</td>
</tr>
<tr class="odd">
<td><code>jax_compact_sparse_BCOO</code></td>
<td>3,207.3388 (12.31)</td>
<td>107.0798</td>
</tr>
<tr class="even">
<td><code>numba_sparse</code></td>
<td>5,548.5175 (21.29)</td>
<td>64.3711</td>
</tr>
<tr class="odd">
<td><code>jax_compact_sparse</code></td>
<td>7,190.9015 (27.59)</td>
<td>35.7355</td>
</tr>
<tr class="even">
<td><code>numba</code></td>
<td>18,187.5003 (69.79)</td>
<td>5,603.6081</td>
</tr>
<tr class="odd">
<td><code>original</code></td>
<td>18,279.9851 (70.15)</td>
<td>6,052.1386</td>
</tr>
<tr class="even">
<td><code>jax_sparse</code></td>
<td>19,786.7200 (75.93)</td>
<td>42.9344</td>
</tr>
<tr class="odd">
<td><code>numba_compact_sparse</code></td>
<td>32,605.2243 (125.12)</td>
<td>248.8764</td>
</tr>
<tr class="even">
<td><code>numba_compact_sparse_direct</code></td>
<td>1,362,329.9249 (&gt;1000.0)</td>
<td>1,366.9112</td>
</tr>
<tr class="odd">
<td><code>original_preload_direct</code></td>
<td>25,218,633.7856 (&gt;1000.0)</td>
<td>8,722.4870</td>
</tr>
</tbody>
</table>
<div class="notes">
<p>On the GPU, however, the <em>dense</em> JAX implementation is the fastest by a large margin — and the sparse implementations that won on a single core are now an order of magnitude behind.</p>
</div>
<!--
```sh
pixi run pytest-benchmark compare --columns=mean,stddev,ops,rounds,iterations --sort=mean 0009_N=32_B=300_K=8192_P=32_S=256_NUM_THREADS=128_cuda
```

```log
---------------------------------------------------- benchmark 'curvature_matrix_DataGenerated': 11 tests ----------------------------------------------------
Name (time in us)                                                               Mean                StdDev                   OPS            Rounds  Iterations
--------------------------------------------------------------------------------------------------------------------------------------------------------------
test_curvature_matrix_jax[DataGenerated]                                    260.5957 (1.0)         29.3714 (1.0)      3,837.3618 (1.0)        1295           1
test_curvature_matrix_jax_BCOO[DataGenerated]                             3,078.2068 (11.81)       35.9463 (1.22)       324.8645 (0.08)          5           1
test_curvature_matrix_jax_compact_sparse_BCOO[DataGenerated]              3,207.3388 (12.31)      107.0798 (3.65)       311.7850 (0.08)        293           1
test_curvature_matrix_numba_sparse[DataGenerated]                         5,548.5175 (21.29)       64.3711 (2.19)       180.2283 (0.05)        146           1
test_curvature_matrix_jax_compact_sparse[DataGenerated]                   7,190.9015 (27.59)       35.7355 (1.22)       139.0646 (0.04)        134           1
test_curvature_matrix_numba[DataGenerated]                               18,187.5003 (69.79)    5,603.6081 (190.78)      54.9828 (0.01)        207           1
test_curvature_matrix_original[DataGenerated]                            18,279.9851 (70.15)    6,052.1386 (206.06)      54.7046 (0.01)          9           1
test_curvature_matrix_jax_sparse[DataGenerated]                          19,786.7200 (75.93)       42.9344 (1.46)        50.5389 (0.01)          6           1
test_curvature_matrix_numba_compact_sparse[DataGenerated]                32,605.2243 (125.12)     248.8764 (8.47)        30.6699 (0.01)         30           1
test_curvature_matrix_numba_compact_sparse_direct[DataGenerated]      1,362,329.9249 (>1000.0)  1,366.9112 (46.54)        0.7340 (0.00)          5           1
test_curvature_matrix_original_preload_direct[DataGenerated]         25,218,633.7856 (>1000.0)  8,722.4870 (296.97)       0.0397 (0.00)          5           1
--------------------------------------------------------------------------------------------------------------------------------------------------------------
```
-->
</section>
</section>
<section id="lesson-learnt-performance-characteristics-and-expectations" class="level1">
<h1>Lesson learnt (performance characteristics and expectations)</h1>
<section id="takeaway-from-benchmark-analysis" class="level2">
<h2 class="anchored" data-anchor-id="takeaway-from-benchmark-analysis">Takeaway from benchmark analysis</h2>
<ul>
<li>“Porting Numba to Numba” is faster in many cases</li>
<li>The only fair fight between Numba and JAX is single CPU core benchmark</li>
<li>Different algorithms of the same function is faster (even across Numba and JAX) depending on input sizes
<ul>
<li><img src="https://latex.codecogs.com/png.latex?%5CRightarrow"> keep all implementations <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> profile <img src="https://latex.codecogs.com/png.latex?%5Crightarrow"> pick best (per science case per system)</li>
</ul></li>
</ul>
<div class="notes">
<p>Improving the algorithm within Numba can often provide significant speedups without needing to switch frameworks at all.</p>
</div>
</section>
<section id="limitation-of-jax-on-cpu" class="level2">
<h2 class="anchored" data-anchor-id="limitation-of-jax-on-cpu">Limitation of JAX on CPU</h2>
<ul>
<li><p>JAX for multithreading on the CPU is a rabbit hole. All links below are from GitHub issues. The lack of documentation reflects on the lack of interest in multicore parallelism on the CPU from the primarily machine learning community (JAX from Google, XLA from OpenXLA).</p>
<p>From <a href="https://github.com/jax-ml/jax/issues/5022#issuecomment-1222336766">JAX running in CPU only mode only uses a single core</a>:</p>
<blockquote class="blockquote">
<p>This is largely working as intended at the moment. JAX doesn’t parallelize operations across CPU cores unless you use explicit parallelism constructs like pmap. Some JAX operations (e.g., BLAS or LAPACK) operations have their own internal parallelism.</p>
</blockquote></li>
<li><p>In an HPC setting, you may want to use multiple hierarchies of parallelism on the CPU: SIMD + Multi-threading (e.g.&nbsp;OpenMP) + Multi-processing (e.g.&nbsp;MPI). In this case, you’d want to limit the number of CPU cores for multi-threading, often set via <code>..._NUM_THREADS</code>. This is very obscure in how to achieve such in JAX:</p>
<ul>
<li><code>XLA_FLAGS='--xla_cpu_multi_thread_eigen=false intra_op_parallelism_threads=1'</code> <a href="https://github.com/jax-ml/jax/issues/743#issuecomment-511684879">was the recommendation</a>. <a href="https://github.com/jax-ml/jax/issues/743#issuecomment-2405731890">It is now recommended to use <code>NPROC=1</code></a> to disable multithreading used in Eigen instead. Notice the lack of <code>JAX_NUM_THREADS</code> <img src="https://latex.codecogs.com/png.latex?%5CRightarrow"> <code>NPROC</code> might have side-effects.</li>
</ul>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode sh code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">MKL_NUM_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span></span>
<span id="cb7-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">MKL_DOMAIN_NUM_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"MKL_BLAS=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<span id="cb7-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">MKL_DYNAMIC</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>FALSE</span>
<span id="cb7-4"></span>
<span id="cb7-5"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OMP_NUM_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span></span>
<span id="cb7-6"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OMP_PLACES</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>threads</span>
<span id="cb7-7"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OMP_PROC_BIND</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>spread</span>
<span id="cb7-8"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OMP_DYNAMIC</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>FALSE</span>
<span id="cb7-9"></span>
<span id="cb7-10"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">NUMEXPR_NUM_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span></span>
<span id="cb7-11"></span>
<span id="cb7-12"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">OPENBLAS_NUM_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span></span>
<span id="cb7-13"></span>
<span id="cb7-14"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">NUMBA_NUM_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span></span>
<span id="cb7-15"></span>
<span id="cb7-16"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">NPROC</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span></span>
<span id="cb7-17"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">JAX_NUM_CPU_DEVICES</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>1</span>
<span id="cb7-18"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">TF_NUM_INTEROP_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>1</span>
<span id="cb7-19"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">export</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">TF_NUM_INTRAOP_THREADS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${NUM_THREADS}</span></span></code></pre></div></div></li>
<li><p>You may set <code>JAX_NUM_CPU_DEVICES=${NUM_THREADS}</code> instead, together with sharding to shard your array to different CPU cores. I.e. OpenMP-like parallelism cannot be achieved. Also, <code>jax.device_put</code> requires your array length is divisible by <code>JAX_NUM_CPU_DEVICES</code>.</p>
<ul>
<li>Preliminary tests are not promising.</li>
</ul></li>
</ul>
</section>
<section id="lessons-learnt-from-numba-vs.-jax" class="level2">
<h2 class="anchored" data-anchor-id="lessons-learnt-from-numba-vs.-jax">Lessons learnt from Numba vs.&nbsp;JAX</h2>
<ul>
<li>Does it solve the “3-implementation problem”?
<ul>
<li>prototype: <code>original</code></li>
<li>multithreading on the CPU: <code>numba</code></li>
<li>running on accelerator: <code>jax</code></li>
</ul></li>
<li>Even if we compare JAX and Numba on equal footing (a single CPU core): sometimes it is faster with JAX (e.g.&nbsp;compiler optimization w.r.t. shape, fusion, etc.) but sometimes it is faster with Numba as JAX language is more restrictive and hence Numba allows expression of more efficient algorithms (recall <img src="https://latex.codecogs.com/png.latex?%5Ctext%7BJAX%7D%20%5Cunderset%7B%5Csim%7D%7B%5Csubset%7D%20%5Ctext%7BNumba%7D">)</li>
<li>Therefore I think it is advantageous to keep both Numba and JAX implementation. See more from <a href="https://ickc.github.io/python-autojax/#should-numba-be-dropped-completely">Should Numba be dropped completely? | AutoJAX Doc</a></li>
</ul>
</section>
<section id="miscellaneous-notes" class="level2">
<h2 class="anchored" data-anchor-id="miscellaneous-notes">Miscellaneous notes</h2>
<ul>
<li>Decouple algorithmic development (“library code”) and Python API, e.g.&nbsp;allow end users to choose backends between Numba and JAX. This also facilitate future porting to, say, Julia.</li>
<li>Open problems: Delaunay/Voronoi mesh is going to be difficult to implement as JAX’s programming model dislikes dynamic shapes.</li>
<li>Don’t wrestle with the language, especially for DSLs</li>
</ul>
</section>
<section id="conclusions" class="level2">
<h2 class="anchored" data-anchor-id="conclusions">Conclusions</h2>
<ul>
<li>Bayesian inference in cosmology is fun!</li>
<li>JAX is a fun(tional) language to work with
<ul>
<li>✓ very easy to deploy to accelerators</li>
<li>✗ more restrictive in language features</li>
<li>✗ poor on CPU multithreading</li>
</ul></li>
</ul>
<div class="notes">
<p>It is a powerful language with a pure functional paradigm that makes it easy to achieve great speedups and deploy on GPUs. But it is not a silver bullet for all HPC use cases, particularly those relying heavily on multi-core CPU parallelism. Thank you.</p>
</div>
</section>
<section id="team-links-references" class="level2">
<h2 class="anchored" data-anchor-id="team-links-references">Team, Links, &amp; References</h2>
<ul>
<li>PyAutoLens Team
<ul>
<li>James W. Nightingale</li>
<li>Richard G. Hayes</li>
<li>Aristeidis Amvrosiadis</li>
<li>Coleman Krawczyk</li>
<li>Gokmen Kilic</li>
<li>…</li>
</ul></li>
<li>Link of this project: <a href="https://ickc.github.io/python-autojax/" class="uri">https://ickc.github.io/python-autojax/</a>. This is a standalone framework to compare different implementations of functions. Functions are being upstreamed to <a href="https://github.com/Jammy2211/pyautolens">PyAutoLens</a>.</li>
</ul>
</section>
<section id="references" class="level2">




</section>
</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-galan_using_2022" class="csl-entry">
Galan, A., G. Vernardos, A. Peel, F. Courbin, and J.-L. Starck. <span>“Using Wavelets to Capture Deviations from Smoothness in Galaxy-Scale Strong Lenses.”</span> <em>Astronomy &amp; Astrophysics</em> 668 (December 2022): A155. <a href="https://doi.org/10.1051/0004-6361/202244464">https://doi.org/10.1051/0004-6361/202244464</a>.
</div>
<div id="ref-gu_giga-lens_2022" class="csl-entry">
Gu, A., X. Huang, W. Sheu, et al. <span>“GIGA-Lens: Fast Bayesian Inference for Strong Gravitational Lens Modeling.”</span> <em>The Astrophysical Journal</em> 935, no. 1 (2022): 49. <a href="https://doi.org/10.3847/1538-4357/ac6de4">https://doi.org/10.3847/1538-4357/ac6de4</a>.
</div>
<div id="ref-mahler_cosmos-web_2025" class="csl-entry">
Mahler, Guillaume, James W. Nightingale, Natalie B. Hogg, et al. <span>“The COSMOS-Web Lens Survey (COWLS) II: Depth, Resolution, and NIR Coverage from JWST Reveal 17 Spectacular Lenses.”</span> arXiv:2503.08782. Preprint, arXiv, March 11, 2025. <a href="https://doi.org/10.48550/arXiv.2503.08782">https://doi.org/10.48550/arXiv.2503.08782</a>.
</div>
<div id="ref-nightingale_scanning_2023" class="csl-entry">
Nightingale, James W., Qiuhan He, Xiaoyue Cao, et al. <span>“Scanning for Dark Matter Subhaloes in <em>Hubble Space Telescope</em> Imaging of 54 Strong Lenses.”</span> <em>Monthly Notices of the Royal Astronomical Society</em> 527, no. 4 (2023): 10480–506. <a href="https://doi.org/10.1093/mnras/stad3694">https://doi.org/10.1093/mnras/stad3694</a>.
</div>
</div></section><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p><span>“Scanning for Dark Matter Subhaloes in <em>Hubble Space Telescope</em> Imaging of 54 Strong Lenses,”</span> <em>Monthly Notices of the Royal Astronomical Society</em> 527, no. 4 (2023): 10480–506, <span>https://doi.org/10.1093/mnras/stad3694</span>.↩︎</p></li>
<li id="fn2"><p>Guillaume Mahler et al., <span>“The COSMOS-Web Lens Survey (COWLS) II: Depth, Resolution, and NIR Coverage from JWST Reveal 17 Spectacular Lenses,”</span> arXiv:2503.08782, preprint, arXiv, March 11, 2025, <span>https://doi.org/10.48550/arXiv.2503.08782</span>.↩︎</p></li>
<li id="fn3"><p>A. Galan et al., <span>“Using Wavelets to Capture Deviations from Smoothness in Galaxy-Scale Strong Lenses,”</span> <em>Astronomy &amp; Astrophysics</em> 668 (December 2022): A155, <span>https://doi.org/10.1051/0004-6361/202244464</span>.↩︎</p></li>
<li id="fn4"><p>A. Gu et al., <span>“GIGA-Lens: Fast Bayesian Inference for Strong Gravitational Lens Modeling,”</span> <em>The Astrophysical Journal</em> 935, no. 1 (2022): 49, <span>https://doi.org/10.3847/1538-4357/ac6de4</span>.↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>Public talk</category>
  <category>JAX</category>
  <category>Durham HPC Days</category>
  <category>JWST</category>
  <guid>https://blog.kolen.dev/RSE/PyAutoLens/2025-06-04-autojax-article.html</guid>
  <pubDate>Wed, 04 Jun 2025 00:00:00 GMT</pubDate>
</item>
<item>
  <title>Quantum Mechanics references</title>
  <dc:creator>Kolen Cheung</dc:creator>
  <link>https://blog.kolen.dev/QM/references/</link>
  <description><![CDATA[ 





<p>TL;DR: I recommend reading chapter 1 of <span class="citation" data-cites="townsend_modern_2012">John S. Townsend.<sup>1</sup></span> And then skim through <span class="citation" data-cites="murayama_many-body_2005">Hitoshi Murayama;<sup>2</sup></span> <span class="citation" data-cites="styer_nine_2002">Daniel F. Styer et al.<sup>3</sup></span> <span class="citation" data-cites="hardy_quantum_2001">Lucien Hardy<sup>4</sup></span> in that order to see if you find anything useful there. The rest are good references if you want to search for a topic.</p>
<section id="physics-textbooks-lecture-notes" class="level1">
<h1>Physics textbooks / lecture notes</h1>
<dl>
<dt><span class="citation" data-cites="griffiths_introduction_2018">David J. Griffiths and Darrell F. Schroeter<sup>5</sup></span></dt>
<dd>
<p>a standard textbook on QM at the undergraduate level.</p>
</dd>
<dt><span class="citation" data-cites="townsend_modern_2012">Townsend<sup>6</sup></span></dt>
<dd>
<p>another textbook on QM at the undergraduate level. The difference with <span class="citation" data-cites="griffiths_introduction_2018">Griffiths and Schroeter<sup>7</sup></span> is how it “bootstrap” QM, i.e.&nbsp;what physical system is used to first introduce the subject. The latter starts with wave function and then dive right into how probability density is related to that. The problem of this approach is it immediately starts with many complicated mathematical concepts as the space is continuous—square-integrable space, probability density, etc. The former bootstrap by starting with the simplest possible non-trivial Hilbert space of dimension 2: spin <img src="https://latex.codecogs.com/png.latex?%5Cfrac%7B1%7D%7B2%7D"> system. I.e. it starts with finite dimensional Hilbert spaces and focuses on the foundational concepts of QM in relation to Hilbert space, before generalizing it to the continuous cases. This all makes sense if you think how statistics should be introduced from discrete random variables before moving on to continuous case which involve much more mathematical subtlety.</p>
</dd>
<dt><span class="citation" data-cites="sakurai_modern_2017">J. J. Sakurai and Jim Napolitano<sup>8</sup></span></dt>
<dd>
<p>a standard textbook on QM at the graduate level.</p>
</dd>
<dt><span class="citation" data-cites="weinberg_lectures_2015">Steven Weinberg<sup>9</sup></span></dt>
<dd>
<p>a graduate textbook on QM by the Nobel Laureate Steven Weinberg. This is a good reference, but don’t expect this to be a light read.</p>
</dd>
<dt><span class="citation" data-cites="murayama_many-body_2005">Murayama<sup>10</sup></span></dt>
<dd>
<p>a graduate level lecture note by a very good teacher, prof. Murayama, on the topic of quantum statistics. It is a gentle introduction on why quantum statistics is the way it is (i.e.&nbsp;why spin implies two different kinds of quantum statistics behavior). It however requires basic understanding of QM so it may not be a good first read as an introduction. Other lecture notes under the same URL are at similar level and usefulness: <a href="http://hitoshi.berkeley.edu/221B/" class="uri">http://hitoshi.berkeley.edu/221B/</a>.</p>
</dd>
</dl>
</section>
<section id="for-mathematicians-theoretical-physicists" class="level1">
<h1>For mathematicians / theoretical physicists</h1>
<dl>
<dt><span class="citation" data-cites="hall_quantum_2013">Brian C. Hall<sup>11</sup></span></dt>
<dd>
<p>introduces QM at graduate level mathematics. It bridges the gap where the mathematically heavy subject, quantum mechanics, is often presented in a not-so-mathematical way. It intermixes both the physical ideas and the mathematical foundation behind the subject. To quote the author:</p>
<blockquote class="blockquote">
<p>The twin goals of the book are (1) to explain the physical ideas of quantum mechanics in language mathematicians will be comfortable with, and (2) to develop the necessary mathematical tools to treat those ideas in a rigorous fashion.</p>
</blockquote>
</dd>
<dt><span class="citation" data-cites="hardy_quantum_2001">Hardy<sup>12</sup></span></dt>
<dd>
<p>an interesting axiomatic approach to quantum theory. 5 axioms are presented, where the first 4 is compatible with “classical probability theory”. I.e. the last axiom leads to quantum theory, providing an insight on what quantum theory is. This provides a more abstract, philosophical background of what QM is, which is often interesting to mathematicians but not so much for a typical physicist. This can be a light read by skimming through the gist of the mathematical ideas behind it.</p>
</dd>
<dt><span class="citation" data-cites="styer_nine_2002">Styer et al.<sup>13</sup></span></dt>
<dd>
<p>another interesting read about the foundation of QM. It outlines 9 different TFAE (The Followings Are Equivalent) ways to formulate QM. It includes for example matrix, wave function, density matrix approaches which is commonly used in QM, and path integral formulation commonly used in QFT (Quantum Field Theory). It can be useful as physicists often use these approaches (especially the first 3) interchangeably which can be confusing to an outsider.</p>
</dd>
</dl>
</section>
<section id="references" class="level1">




</section>


<div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-bibliography"><h2 class="anchored quarto-appendix-heading">References</h2><div id="refs" class="references csl-bib-body hanging-indent">
<div id="ref-griffiths_introduction_2018" class="csl-entry">
Griffiths, David J., and Darrell F. Schroeter. <em>Introduction to Quantum Mechanics</em>. 3rd ed. Cambridge University Press, 2018. <a href="https://doi.org/10.1017/9781316995433">https://doi.org/10.1017/9781316995433</a>.
</div>
<div id="ref-hall_quantum_2013" class="csl-entry">
Hall, Brian C. <em>Quantum Theory for Mathematicians</em>. Vol. 267. Graduate Texts in Mathematics. Springer New York, 2013. <a href="https://doi.org/10.1007/978-1-4614-7116-5">https://doi.org/10.1007/978-1-4614-7116-5</a>.
</div>
<div id="ref-hardy_quantum_2001" class="csl-entry">
Hardy, Lucien. <span>“Quantum Theory From Five Reasonable Axioms.”</span> arXiv:quant-ph/0101012. Preprint, arXiv, September 25, 2001. <a href="https://doi.org/10.48550/arXiv.quant-ph/0101012">https://doi.org/10.48550/arXiv.quant-ph/0101012</a>.
</div>
<div id="ref-murayama_many-body_2005" class="csl-entry">
Murayama, Hitoshi. <span>“Many-Body Problems I (Quantum Statistics).”</span> February 28, 2005. <a href="http://hitoshi.berkeley.edu/221B/statistics.pdf">http://hitoshi.berkeley.edu/221B/statistics.pdf</a>.
</div>
<div id="ref-sakurai_modern_2017" class="csl-entry">
Sakurai, J. J., and Jim Napolitano. <em>Modern Quantum Mechanics</em>. 2nd ed. Cambridge University Press, 2017. <a href="https://doi.org/10.1017/9781108499996">https://doi.org/10.1017/9781108499996</a>.
</div>
<div id="ref-styer_nine_2002" class="csl-entry">
Styer, Daniel F., Miranda S. Balkin, Kathryn M. Becker, et al. <span>“Nine Formulations of Quantum Mechanics.”</span> <em>American Journal of Physics</em> 70, no. 3 (2002): 288–97. <a href="https://doi.org/10.1119/1.1445404">https://doi.org/10.1119/1.1445404</a>.
</div>
<div id="ref-townsend_modern_2012" class="csl-entry">
Townsend, John S. <em>A Modern Approach to Quantum Mechanics</em>. 2. ed. University Science Books, 2012.
</div>
<div id="ref-weinberg_lectures_2015" class="csl-entry">
Weinberg, Steven. <em>Lectures on Quantum Mechanics</em>. 2nd ed. Cambridge University Press, 2015. <a href="https://doi.org/10.1017/CBO9781316276105">https://doi.org/10.1017/CBO9781316276105</a>.
</div>
</div></section><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p><em>A Modern Approach to Quantum Mechanics</em>, 2. ed (University Science Books, 2012).↩︎</p></li>
<li id="fn2"><p><span>“Many-Body Problems I (Quantum Statistics),”</span> February 28, 2005, <span>http://hitoshi.berkeley.edu/221B/statistics.pdf</span>.↩︎</p></li>
<li id="fn3"><p><span>“Nine Formulations of Quantum Mechanics,”</span> <em>American Journal of Physics</em> 70, no. 3 (2002): 288–97, <span>https://doi.org/10.1119/1.1445404</span>.↩︎</p></li>
<li id="fn4"><p><span>“Quantum Theory From Five Reasonable Axioms,”</span> arXiv:quant-ph/0101012, preprint, arXiv, September 25, 2001, <span>https://doi.org/10.48550/arXiv.quant-ph/0101012</span>.↩︎</p></li>
<li id="fn5"><p><em>Introduction to Quantum Mechanics</em>, 3rd ed. (Cambridge University Press, 2018), <span>https://doi.org/10.1017/9781316995433</span>.↩︎</p></li>
<li id="fn6"><p><em>A Modern Approach to Quantum Mechanics</em>.↩︎</p></li>
<li id="fn7"><p><em>Introduction to Quantum Mechanics</em>.↩︎</p></li>
<li id="fn8"><p><em>Modern Quantum Mechanics</em>, 2nd ed. (Cambridge University Press, 2017), <span>https://doi.org/10.1017/9781108499996</span>.↩︎</p></li>
<li id="fn9"><p><em>Lectures on Quantum Mechanics</em>, 2nd ed. (Cambridge University Press, 2015), <span>https://doi.org/10.1017/CBO9781316276105</span>.↩︎</p></li>
<li id="fn10"><p><span>“Many-Body Problems”</span>.↩︎</p></li>
<li id="fn11"><p><em>Quantum Theory for Mathematicians</em>, vol. 267, Graduate Texts in Mathematics (Springer New York, 2013), <span>https://doi.org/10.1007/978-1-4614-7116-5</span>.↩︎</p></li>
<li id="fn12"><p><span>“Quantum Theory From Five Reasonable Axioms”</span>.↩︎</p></li>
<li id="fn13"><p><span>“Nine Formulations of Quantum Mechanics”</span>.↩︎</p></li>
</ol>
</section></div> ]]></description>
  <category>References</category>
  <category>QM</category>
  <guid>https://blog.kolen.dev/QM/references/</guid>
  <pubDate>Fri, 11 Apr 2025 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
