<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>cielbird</title>
    <subtitle>blog &amp; portfolio</subtitle>
    <link rel="self" type="application/atom+xml" href="https://cielbird.github.io/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://cielbird.github.io"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-04-26T00:00:00+00:00</updated>
    <id>https://cielbird.github.io/atom.xml</id>
    <entry xml:lang="en">
        <title>Designing a 12 bit processor</title>
        <published>2026-04-26T00:00:00+00:00</published>
        <updated>2026-04-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/blog/r12-layout/"/>
        <id>https://cielbird.github.io/blog/r12-layout/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/blog/r12-layout/">&lt;p&gt;I wanted to post this beauty here.&lt;&#x2F;p&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;layout.53c3163373c0abf1.png&quot; alt=&quot;R12 CPU Layout&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;This is a 12 bit processor, designed from the ground up, with the help of a friend.
It&#x27;s my first time designing an integrated circuit this large and complicated.&lt;&#x2F;p&gt;
&lt;p&gt;What you&#x27;re seeing is the layout for fabrication on silicon on a 180nm TSMC process.
Each most colors here are different metal interconnect layers.
Below them in blue are TSMC&#x27;s logic cells.
The inputs and outputs go off the screen. Those are the data and address bus lines.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m pretty satisfied with the performance. With my limited setup I was able to design it for a
whopping 28.6MHz clock speed ! That takes into account all sorts of delays due to logic cell
capacitances and interconnect parasitics.&lt;&#x2F;p&gt;
&lt;p&gt;And yes, it works. Here&#x27;s a simple program running :&lt;&#x2F;p&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;modelsim_sim.4d60ecebc3acebd5.png&quot; alt=&quot;R12 CPU Sim&quot; loading=&quot;lazy&quot;&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Modeling a conlang&#x27;s phonology</title>
        <published>2026-04-25T00:00:00+00:00</published>
        <updated>2026-04-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/blog/modeling-phonology/"/>
        <id>https://cielbird.github.io/blog/modeling-phonology/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/blog/modeling-phonology/">&lt;h1 id=&quot;context&quot;&gt;Context&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;ve worked on making a couple fictional languanges in the past. They&#x27;re known as
&lt;a href=&quot;https:&#x2F;&#x2F;cielbird.github.io&#x2F;blog&#x2F;modeling-phonology&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Constructed_language&quot;&gt;conlangs&lt;&#x2F;a&gt;, and there&#x27;s a whole online community
around the art of constructing fictional languages.&lt;&#x2F;p&gt;
&lt;p&gt;Something people like to do is to &quot;evolve&quot; their languages to create dialects or descendent
languages. You apply phonological and grammatical changes to the language to create a new one,
much like real languages change over time.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;getheode-the-project&quot;&gt;Geþeode: the project&lt;&#x2F;h1&gt;
&lt;p&gt;After tediously applying changes to words in my fictional languages, I wanted to make software to
automate the process. I called this project &quot;geþeode&quot;, after an Old English word meaning language.&lt;&#x2F;p&gt;
&lt;p&gt;I started with phonological changes, or sound changes, as they are pretty predictable. They can
also be pretty simple.
The same sound changes often occur across different languages and in different time periods.
As an example, here&#x27;s a change that happened from classical latin (~200 BCE) to vulgar
latin (~500 BCE) :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;kʷ → ɡʷ &#x2F; V_V&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is a pretty standard notation. Here it means a &quot;kw&quot; sound became voiced when it occurred
in contexts between two vowels (V). Words like &quot;aqua&quot; [ˈaɡ.wa] became pronounced more like
&quot;agua&quot; [ˈaɡ.wa]. Modern Spanish &quot;agua&quot; [ˈaɣ.wa] inherited from this change.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re unfamiliar with this ([ˈaɣ.wa]) notation, it&#x27;s the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;International_Phonetic_Alphabet&quot;&gt;International Phonetic Alphabet&lt;&#x2F;a&gt;.
Parsing IPA notation was a large part of this project.&lt;&#x2F;p&gt;
&lt;p&gt;An important part of this was to stay close to real linguistic notation. I didn&#x27;t want to invent my
own notation, which is something most other tools do. For reference, I used
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chridd.nfshost.com&#x2F;diachronica&#x2F;&quot;&gt;this amazing compilation&lt;&#x2F;a&gt; of sound
changes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;development&quot;&gt;Development&lt;&#x2F;h2&gt;
&lt;p&gt;Being a performance obsessed low-level software guy, I decided to code everything in Rust.&lt;&#x2F;p&gt;
&lt;p&gt;I used &lt;code&gt;nom&lt;&#x2F;code&gt; for parsing, which has been such a great experience. I will never write my own parser
again in rust !&lt;&#x2F;p&gt;
&lt;h2 id=&quot;universal-phonological-model&quot;&gt;Universal phonological model&lt;&#x2F;h2&gt;
&lt;p&gt;Linguists can&#x27;t agree on how language sounds exist and work in our brains, so there&#x27;s no &quot;universal&quot;
phonological model. However, there&#x27;s concensus on enough stuff for me to pick a middle ground.
Linguists agree that all phonological sounds (called segments) can be defined as sets of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Distinctive_feature&quot;&gt;distinctive features&lt;&#x2F;a&gt;. Either the feature is
present (+) or not (-), so they&#x27;re binary.&lt;&#x2F;p&gt;
&lt;p&gt;Most other sound change tools like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.zompist.com&#x2F;sca2.html&quot;&gt;SCA2&lt;&#x2F;a&gt; require you to
essentially define the phonological model you use. They don&#x27;t define a universal set of
phonological features, and they&#x27;re basically fancy text editing engines. For many cases, it&#x27;s quite
enough.&lt;&#x2F;p&gt;
&lt;p&gt;A pretty defining trait of my tool is that I &lt;em&gt;assume&lt;&#x2F;em&gt; a certain phonological model, and try to
accommodate for all use cases.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;trees-not-strings&quot;&gt;Trees, not strings&lt;&#x2F;h2&gt;
&lt;p&gt;Most features define individual segments. However, some features describe syllables,
and some describe groups of syllables (called feet). These are
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Prosody_(linguistics)#Phonology&quot;&gt;suprasegmental features&lt;&#x2F;a&gt;, and are
usually things like stress, or tone. They&#x27;re actually pretty few in number compared to regular
segmental features, so it&#x27;s easy to put them aside. All the sound change software tools I&#x27;ve found
ignore these, or deal with them in hacky ways.
But if you account for them, the model of a phonological sequence completely changes.
It goes from a simple string to &lt;strong&gt;a tree&lt;&#x2F;strong&gt;. Here&#x27;s &quot;bailar&quot; (Spanish, to dance) decomposed
for illustration :&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [ _ ]              &amp;lt; word or foot level features&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       &#x2F;      \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; [-stress]     [+stress]    &amp;lt; syllable level features&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; |   |   |     |    |   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[b] [a] [ɪ]   [l]  [a]  [ɾ]   &amp;lt; segment level features&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The model is a tree, where each node has a set of features relative to it&#x27;s depth, and where
each leaf is at the maximum depth.&lt;&#x2F;p&gt;
&lt;p&gt;Some questions are hard to answer :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How many levels in the tree ?
&lt;ul&gt;
&lt;li&gt;Can sound changes consider &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;glottopedia.org&#x2F;index.php&#x2F;Foot&quot;&gt;prosodic foot&lt;&#x2F;a&gt; ?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Which features should exist ?
&lt;ul&gt;
&lt;li&gt;What binary features should be used to describe different rising&#x2F;falling tones ?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;rules&quot;&gt;Rules&lt;&#x2F;h2&gt;
&lt;p&gt;Now since the phonological sequence is a tree, sound changes must be interpreted as tree operations.&lt;&#x2F;p&gt;
&lt;p&gt;Getheode actually compiles sound changes into a set of search and replace operations that not
only match segment features, but also surrounding and internal tree structure. This model allows
for much easier application of sound changes.&lt;&#x2F;p&gt;
&lt;p&gt;For example, the following rule only applies in certain word-boundary contexts :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;t -&amp;gt; d &#x2F; V#_V&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here, a [t] sound becomes voiced, becoming [d], when it is word-initial (# indicates word boundary),
and where the surrounding sounds are vowels.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;branching&quot;&gt;Branching&lt;&#x2F;h2&gt;
&lt;p&gt;Getheode also supports optionals or branches in the rule. For example :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;{n,q,h} -&amp;gt; Ø &#x2F; _(s)a&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here, either a [n], a [q], or a [h] are removed, before a [a] or a [sa]. This rule is imaginary
and very unlikely. But it works in Getheode.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tagging&quot;&gt;Tagging&lt;&#x2F;h2&gt;
&lt;p&gt;The tool supports manually tagging features. For example :&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;V_0ʔV_0 -&amp;gt; Vː_0&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here, we want two vowels (V) that are the same (both have _0) that surround a glottal stop
(ʔ) merge into one long (ː) vowel , removing the glottal stop.&lt;&#x2F;p&gt;
&lt;p&gt;(The glottal stop is the sound you make when you go &quot;a, a, a&quot; = [ʔaʔaʔa])&lt;&#x2F;p&gt;
&lt;p&gt;Under this sound change, [aʔaʔila] becomes [aːʔila].&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-largest-issue&quot;&gt;The largest issue&lt;&#x2F;h2&gt;
&lt;p&gt;Many sound change rules ignore syllable and word boundaries. However, a rule&#x27;s output cannot be
ambiguous, and it cannot be easily inferred. So, this means that some rules are interpreted more
strictly than they should, which I&#x27;ll talk about in a different post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;The project&#x27;s page is &lt;a href=&quot;https:&#x2F;&#x2F;cielbird.github.io&#x2F;projects&#x2F;getheode&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;. Most rules are applied correctly.&lt;&#x2F;p&gt;
&lt;p&gt;I will make a WASM web demo soon.&lt;&#x2F;p&gt;
&lt;p&gt;Next, I will tackle &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Phonotactics&quot;&gt;phonotactics&lt;&#x2F;a&gt;,
which are the rules that govern how phonemes can be arranged in
words in different languages. I&#x27;d like to apply a set of sound changes on not only a set of words,
but a generalized phonotactic rule set.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Writing a Minecraft clone in Rust</title>
        <published>2026-01-15T00:00:00+00:00</published>
        <updated>2026-01-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/blog/mc-clone/"/>
        <id>https://cielbird.github.io/blog/mc-clone/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/blog/mc-clone/">&lt;h1 id=&quot;context&quot;&gt;Context&lt;&#x2F;h1&gt;
&lt;p&gt;This project started 10 months ago. I wanted to learn more about Rust, and see what it took to
write a custom game engine.&lt;&#x2F;p&gt;
&lt;p&gt;And because I grew up with Minecraft, I wanted to shoot my shot at making a Minecraft clone myself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tools&quot;&gt;Tools&lt;&#x2F;h2&gt;
&lt;p&gt;I could have used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bevy.org&#x2F;&quot;&gt;Bevy&lt;&#x2F;a&gt; as a game engine, but to stay in the spirit of
re-inventing the wheel, I decided to make my own game engine.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll surely write another couple posts about all interesting the design choices and bugs I
encountered when making my engine, but here I wanted to talk about the block game I&#x27;ve started.&lt;&#x2F;p&gt;
&lt;p&gt;I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wgpu.rs&#x2F;&quot;&gt;wgpu&lt;&#x2F;a&gt; for rendering, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-windowing&#x2F;winit&quot;&gt;winit&lt;&#x2F;a&gt; for
window handling, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;cgmath&#x2F;latest&#x2F;cgmath&#x2F;&quot;&gt;cgmath&lt;&#x2F;a&gt; for vector and matrix math.&lt;&#x2F;p&gt;
&lt;p&gt;I could have made a backend with Vulcan if I wanted peak efficiency, but a game like minecraft
isn&#x27;t very GPU intensive. Being cross-platform is important to me, so &lt;code&gt;wgpu&lt;&#x2F;code&gt; was the natural choice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mesh-generation&quot;&gt;Mesh generation&lt;&#x2F;h2&gt;
&lt;p&gt;This is pretty simple but tedious code to write. Each possible 6 faces have a separate case.
Nothing much interesting here. I did spend a lot of time structuring my engine for mesh generation
this to be possible outside of the actual engine code. I don&#x27;t want to be working with Wgpu
buffers outside my engine code!&lt;&#x2F;p&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;voxel_mesh.eb0bf17751c3e27f.png&quot; alt=&quot;Voxel mesh example&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;&lt;em&gt;This is what my voxel generation looks like now.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rotating-blocks&quot;&gt;Rotating blocks&lt;&#x2F;h2&gt;
&lt;p&gt;I wanted to have a block represent the corner of a wood log. I also wanted to be able to rotate or
flip these blocks any way possible. Minecraft allows certain blocks to be rotated in certain ways,
but it&#x27;s a bit hard-coded. I wanted full rotation freedom for any block.
Mathematically, this could be represented by a 3x3 transformation matrix.
I also wanted this orientation data to be as small as possible: so 3x3 &lt;code&gt;f32&lt;&#x2F;code&gt; arrays are out of the
question. I also wanted to avoid unnecessary floating point operations.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote my own 3x3 matrix class that could only have orthonormal axis-aligned transformations.
No stretching, no diagonal blocks, no warping, only 90 degree turns and flips across an axis.
I could avoid 3x3 float matrices, saving a lot on operations and memory, all while using the
same useful operators like &lt;code&gt;Mul&lt;&#x2F;code&gt; and &lt;code&gt;Add&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Rust&#x27;s traits and enums make this really intuitive:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;repr&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;i8&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; so we can do `as i8`, fast conversion&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; enum&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Sign&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Neg&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Zero&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;    Pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;&#x2F;&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; An orthonormal axis-aligned transform&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; VoxelTransform&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Sign&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Using matrices for this allowed me to apply these transformations to the UV coordinates I picked
for each block face, so I could rotate UV coordinates as needed if a block was rotated.&lt;&#x2F;p&gt;
&lt;p&gt;This allowed me to do fun things that even Minecraft doesn&#x27;t do. For example :&lt;&#x2F;p&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;voxels_rotated.0009da1fe07e85de.png&quot; alt=&quot;Voxels Rotated Example&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;&lt;em&gt;Here, I&#x27;m not rotating the voxel model (actually, they&#x27;re all part of the same model),&lt;&#x2F;em&gt;
&lt;em&gt;I&#x27;m just changing the UV mapping of the mesh!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;collider-generation&quot;&gt;Collider generation&lt;&#x2F;h2&gt;
&lt;p&gt;At this point I didn&#x27;t have any colliders in my engine or my game, so it wasn&#x27;t very interesting
to play. I looked into several different collision algorithms.&lt;&#x2F;p&gt;
&lt;p&gt;Collision is a really heavy task, especially when it needs to be done 60 times a
second. Some questions you need to ask to have faster collision :&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Does my collider have a certain shape?
&lt;ul&gt;
&lt;li&gt;Spheres are very simple to check, for example.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Is my collider concave or convex?
&lt;ul&gt;
&lt;li&gt;With convex colliders, you can use the separating axis theorem.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In the case of voxels, we have axis-aligned bounding boxes (AABB), which are actually one of the
simplest kinds of collisions to check.
You just compare min&#x2F;max coordinates along each axis to see if two boxes overlap.&lt;&#x2F;p&gt;
&lt;p&gt;I started the naive way, by adding one collider for each voxel. As expected, it was slow. Less
than 1 FPS with a couple thousand voxels. I knew I could optimize the algorithm to merge voxels.
If I have a 3x3x3 solid block of voxels, I can have one AABB collider instead of 27. For example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|0  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---++---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|1  ||2  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|   ||   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---++---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---++---++---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|3  ||4  ||5  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|   ||   ||   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---++---++---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Becomes ...&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|1  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+--------+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|2       |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|        |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|        |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|        |+---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|        ||3  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|        ||   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+--------++---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The problem to finding the true minimum number of boxes for a given voxel grid is NP-hard.
But we can use a good greedy solution to get reasonable performance gains.&lt;&#x2F;p&gt;
&lt;p&gt;This was a very contained optimization so I decided to give the problem to AI. It spat out a
solution that was written as I wanted it (always check your bot&#x27;s homework !).
I went from about 1 FPS to a solid 20 FPS. Adding compilation optimizations, I had a smooth &amp;gt;60 FPS.
Thanks Chat!&lt;&#x2F;p&gt;
&lt;p&gt;I still have many, many optimizations to add to my engine, and my future blogs will be about those.
Two that come to mind now are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Caching important components like Transforms, Models, and Colliders&lt;&#x2F;li&gt;
&lt;li&gt;Organizing colliders in faster structures (bounding volume hierarchy, specifically)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Stay tuned!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Distributed AI in Rust</title>
        <published>2025-09-01T00:00:00+00:00</published>
        <updated>2025-10-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/blog/burn-distributed/"/>
        <id>https://cielbird.github.io/blog/burn-distributed/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/blog/burn-distributed/">&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h1&gt;
&lt;p&gt;AI models and their training data are big. Training an AI model on only one GPU can become slow, and many models don&#x27;t even fit on one single GPU. Many techniques exist to manage the memory and speed limitations of modern models. These techniques have unlocked many of the impressive advances in machine learning we see today. These training and inference techniques all depend on basic building blocks of collective communications called &lt;em&gt;collective operations&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In this blog we will cover collective operations, how they are used to speed up training. We&#x27;ll also discuss the unique way &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;burn.dev&#x2F;&quot;&gt;Burn&lt;&#x2F;a&gt; implements collective operations in the 0.20.0 release, as well as how you can easily train your models on multiple devices and multiple nodes.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;data-parallel-training&quot;&gt;Data Parallel Training&lt;&#x2F;h1&gt;
&lt;p&gt;Distributed data parallel training (DDP) is a basic example of distributed training. It consists of splitting the training data between multiple devices and training a copy of the model on each device, all while keeping the parameters in sync.&lt;&#x2F;p&gt;
&lt;p&gt;First, the training data is split between each device. Then, during each training step, each device does a forward and backward pass on its own batch of data. The resulting gradients are aggregated between each device. Each device then optimizes its model with the
new gradients. At this point the models have the same optimized parameters.&lt;&#x2F;p&gt;
&lt;!-- ![distributed-data-parallel](ddp.png) --&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;ddp.d74a376b3f1bd13d.png&quot; alt=&quot;Distributed Data Parallel&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;&lt;em&gt;Figure 1: Data distributed training on three devices&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This technique allows training time to be cut down significantly, as long as the gradient syncing
is negligible. This technique still requires each device to store the entire
model in memory, which is an issue tackled by other distributed training techniques.&lt;&#x2F;p&gt;
&lt;p&gt;It is clear that the key to this technique is the gradient syncing. The gradient syncing must
be as efficient as possible as to not be a bottleneck in the pipeline.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;The term &#x27;devices&#x27; refers to GPUs, TPUs, and other computational hardware commonly utilized in machine learning applications.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;h1 id=&quot;collective-operations&quot;&gt;Collective operations&lt;&#x2F;h1&gt;
&lt;p&gt;The syncing of gradients in a data parallel training is a collecive operation called an &lt;em&gt;all-reduce&lt;&#x2F;em&gt;. An all-reduce is one of many primitive collective operations. Some others are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;broadcast: one device sends a tensor to all others&lt;&#x2F;li&gt;
&lt;li&gt;reduce: a tensor on each device is reduced to one tensor on one device&lt;&#x2F;li&gt;
&lt;li&gt;reduce-scatter: a tensor on each device is reduced, each device ends up with a part of the resulting tensor&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;PyTorch and TensorFlow don&#x27;t implement their own collective operations, instead they make use of communication libraries such as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.nvidia.com&#x2F;nccl&quot;&gt;NCCL&lt;&#x2F;a&gt; (for NVIDIA GPUs), MPI, or Gloo.&lt;&#x2F;p&gt;
&lt;p&gt;NCCL is the library used for NVIDIA GPUs. It abstracts collective operations using protocols like NVLink, PCIe, GPUDirect RDMA, and even TCP&#x2F;IP. For all of NCCL&#x27;s benefits, it is only useful for NVIDIA devices, which goes against Burn&#x27;s core principles.&lt;&#x2F;p&gt;
&lt;p&gt;Moreover, Burn &lt;em&gt;already has&lt;&#x2F;em&gt; the tools for tensor communication between devices on the same machine with &lt;code&gt;Tensor::to_device&lt;&#x2F;code&gt;. We can take advantage of shared memory, or even backend specific protocols like &lt;code&gt;NVLink&lt;&#x2F;code&gt; for an Nvidia backend. Logically, GPU-to-GPU communication on the same machine should be done with &lt;code&gt;to_device&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For these reasons, we decided to implement our own collective operations crate called &lt;code&gt;burn-collective&lt;&#x2F;code&gt;. For intra-node communication, we use &lt;code&gt;Tensor::to_device&lt;&#x2F;code&gt;, taking advantage of all the backend specific optimisations. For inter-node communication, we use TCP&#x2F;IP. This two-step separation will show up later.&lt;&#x2F;p&gt;
&lt;!-- ![stack](stack.png) --&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;stack.b35af59720a8415b.png&quot; alt=&quot;Stack&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;&lt;em&gt;Figure 2: Pytorch and NCCL compared to Burn&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;how-burn-implements-collective-operations&quot;&gt;How Burn implements collective operations&lt;&#x2F;h1&gt;
&lt;p&gt;Burn currently only supports all-reduce. Reduce and broadcast are also supported, although only in single-node contexts.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-many-processes&quot;&gt;How many processes?&lt;&#x2F;h2&gt;
&lt;p&gt;We started with &lt;code&gt;all-reduce&lt;&#x2F;code&gt;, because it is the backbone to data distributed training.&lt;&#x2F;p&gt;
&lt;p&gt;With PyTorch, you usually assign a different process to each GPU. There are many reasons for this, but a big one is the Python&#x27;s Global Interpreter Lock (GIL). The GIL only allows one thread to hold the Python interpreter at a time, which essentially prevents anything written in Python to actually be multi-threaded.&lt;&#x2F;p&gt;
&lt;p&gt;Thankfully, we&#x27;re not using Python.&lt;&#x2F;p&gt;
&lt;p&gt;As said before, we can use &lt;code&gt;to_device&lt;&#x2F;code&gt; to take care of intra-node communication. We can assume the user will launch a thread for each GPU. So for one machine, we only need one process.&lt;&#x2F;p&gt;
&lt;!-- ![burn_collective_architecture](burn_collective.png) --&gt;
&lt;p&gt;
&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;burn_collective.8edfe3c33abf9782.png&quot; alt=&quot;Burn collective&quot; loading=&quot;lazy&quot;&gt;

&lt;em&gt;Figure 3: Burn collective: an example structure with 4 peers and 2 nodes&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;local-and-global&quot;&gt;Local and global&lt;&#x2F;h2&gt;
&lt;p&gt;Since intra-node and inter-node communication are fundamentally different, we decided to split collective operations between a &lt;em&gt;local&lt;&#x2F;em&gt; (intra-node) and &lt;em&gt;global&lt;&#x2F;em&gt; (inter-node) level. Internally, the algorithms are implemented differently on the internal level and global levels.&lt;&#x2F;p&gt;
&lt;p&gt;This leads to a process-per-node structure.&lt;&#x2F;p&gt;
&lt;p&gt;It is worth noting that the local&#x2F;global separation is an implementation detail, and it is only necessary to know when configuring the collective. From a user&#x27;s perspective, all the other peers, whether on the same node or not, are just as accessible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;walkthrough-of-an-all-reduce&quot;&gt;Walkthrough of an All-Reduce&lt;&#x2F;h2&gt;
&lt;p&gt;Lets walk through the internals of a call to &lt;code&gt;all_reduce&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Each thread must first register, passing a &lt;code&gt;CollectiveConfig&lt;&#x2F;code&gt; that contains information about the number of peers on the same node, as well as the number of nodes in the collective. The call to &lt;code&gt;register&lt;&#x2F;code&gt; is blocking, so it syncs all the threads. When the node&#x27;s &lt;code&gt;LocalCollectiveServer&lt;&#x2F;code&gt; has registered each peer on the node, the node will register on the global level if necessary.&lt;&#x2F;p&gt;
&lt;p&gt;Then, on the global level, the &lt;code&gt;GlobalOrchestrator&lt;&#x2F;code&gt; acts as a rendez-vous point for each node. After registering, the nodes have the addresses of every other node in the collective, and they can be as independent as possible. In the future, the &lt;code&gt;GlobalOrchestrator&lt;&#x2F;code&gt; could allow for a dynamic topology, keeping nodes updated on any changes.&lt;&#x2F;p&gt;
&lt;p&gt;Next, all peers in the collective call an &lt;code&gt;all_reduce&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;When all registered threads have called the opration, the &lt;code&gt;LocalCollectiveServer&lt;&#x2F;code&gt; starts the operation. In single node contexts, this is very simple, as the &lt;code&gt;LocalCollectiveServer&lt;&#x2F;code&gt; manages everything with &lt;code&gt;Tensor::to_device&lt;&#x2F;code&gt; for tensor transfers.&lt;&#x2F;p&gt;
&lt;p&gt;In a multi-node context, each node will already have the coordinates of other nodes, supplied upon registering. They communicate tensors with the &lt;code&gt;burn_communications&lt;&#x2F;code&gt; crate, specifically with the &lt;code&gt;TensorDataService&lt;&#x2F;code&gt;. This service allows for exposing and downloading Burn tensors over the network in a peer-to-peer manner. Currently we use WebSockets, but QUIC is a likely candidate for future use.&lt;&#x2F;p&gt;
&lt;p&gt;In multi-node contexts, nodes must synchronise at the end of the operation. This is true for all collective operations, but it becomes especially important for &lt;code&gt;broadcast&lt;&#x2F;code&gt;, where the broadcaster must wait for all receivers to receive the tensor.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;methods&quot;&gt;Methods&lt;&#x2F;h2&gt;
&lt;p&gt;Burn supports multiple strategies for all-reduce, configurable at both local and global levels.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;centralized&quot;&gt;Centralized&lt;&#x2F;h3&gt;
&lt;p&gt;All peers send tensors to a root, which aggregates them and broadcasts the result back.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tree&quot;&gt;Tree&lt;&#x2F;h3&gt;
&lt;p&gt;Peers are arranged in a b-tree and reduce in parallel, achieving $O(\log_b(N))$ time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ring&quot;&gt;Ring&lt;&#x2F;h3&gt;
&lt;p&gt;Peers form a ring, slicing tensors and passing them around. This maximizes bandwidth usage but is more sensitive to latency.&lt;&#x2F;p&gt;
&lt;!-- ![all-reduce-methods](methods.png) --&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;methods.3a8a49145800aec8.png&quot; alt=&quot;all-reduce methods&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;&lt;em&gt;Figure 4: An overview of the three strategies&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;local-strategy-and-global-strategy&quot;&gt;Local strategy and global strategy&lt;&#x2F;h3&gt;
&lt;p&gt;Since the all-reduce is split on two levels, the local (intra-node) level and global (inter-node) level, we can use different local strategies for different nodes, and a different strategy on the global level. Below is a diagram that shows an example of a collective with 3 nodes, each using a different local strategy.&lt;&#x2F;p&gt;
&lt;!-- ![all-reduce-methods-local-global](method_local_global.png) --&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;method_local_global.b732f44bf98fca4e.png&quot; alt=&quot;all-reduce-methods-local-global&quot; loading=&quot;lazy&quot;&gt;
&lt;h3 id=&quot;local-ring-downfall&quot;&gt;Local ring downfall&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;Centralized&lt;&#x2F;code&gt; and &lt;code&gt;Tree&lt;&#x2F;code&gt; strategies can be split into two operations: a reduce and a broadcast. A reduce operation aggregates all tensors onto one peer, and a broadcast distributes a tensor from one peer to all others.&lt;&#x2F;p&gt;
&lt;p&gt;The result from the global all-reduce needs to be broadcast to all other local peers. So, with &lt;code&gt;Ring&lt;&#x2F;code&gt; and &lt;code&gt;Centralized&lt;&#x2F;code&gt; we don&#x27;t actually need to do a local all-reduce, we just need to do a reduce, followed by the global all-reduce, followed by the broadcast. It&#x27;s like fitting the global all-reduce in the middle of the local all-reduce&lt;&#x2F;p&gt;
&lt;p&gt;So with &lt;code&gt;Centralized&lt;&#x2F;code&gt; and &lt;code&gt;Tree&lt;&#x2F;code&gt; in multi-node contexts we do:&lt;&#x2F;p&gt;
&lt;p&gt;Local reduce -&amp;gt; Global all-reduce -&amp;gt; Local broadcast&lt;&#x2F;p&gt;
&lt;p&gt;Due to the nature of the &lt;code&gt;Ring&lt;&#x2F;code&gt; algorithm, a ring-reduce can&#x27;t be split between a reduce step and a broadcast step. This means if the &lt;code&gt;Ring&lt;&#x2F;code&gt; strategy is chosen locally, the steps will be as follows:&lt;&#x2F;p&gt;
&lt;p&gt;Local all-reduce -&amp;gt; Global all-reduce -&amp;gt; Local broadcast&lt;&#x2F;p&gt;
&lt;p&gt;This unnecessarily distributes the local all-reduce result to local peers, when anyway we will overwrite the tensor with the global all-reduce result. This may be less advantageous than other configurations. For this reason, it is recommended not to use &lt;code&gt;Ring&lt;&#x2F;code&gt; on the local level, only on the global level.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;burn-communications&quot;&gt;&lt;code&gt;burn-communications&lt;&#x2F;code&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;With the addition of &lt;code&gt;burn-collective&lt;&#x2F;code&gt;, it was necessary to build a solid base for network communication in burn. The &lt;code&gt;burn-communications&lt;&#x2F;code&gt; crate offers an abstraction of client-server logic, as well as a &lt;code&gt;TensorDataService&lt;&#x2F;code&gt; used for peer-to-peer tensor transfers. This allows developers to swap protocols with minimal effort.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;ddp-training&quot;&gt;DDP Training&lt;&#x2F;h1&gt;
&lt;p&gt;Lets get back to a Data Parallel training. How can you take advantage of these fancy new collective operations?&lt;&#x2F;p&gt;
&lt;p&gt;Previously, to train on multiple devices, you had to use the &lt;code&gt;LearnerBuilder::devices&lt;&#x2F;code&gt; function:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; learner&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; LearnerBuilder&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;ARTIFACT_DIR&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;devices&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;vec!&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;gpu_1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gpu_2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gpu_3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;optimizer&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This has been replaced with &lt;code&gt;LearnerBuilder::learning_strategy&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; collective&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; CollectiveConfig&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; learner&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; LearnerBuilder&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;ARTIFACT_DIR&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;learning_strategy&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;burn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;train&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;ddp&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;vec!&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;gpu_1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gpu_2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gpu_3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; collective&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;optimizer&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The DDP learning strategy will launch a thread for each device, so in single-node environments
this is a minimal change.&lt;&#x2F;p&gt;
&lt;p&gt;For multi-node environments, the user will need to launch the &lt;code&gt;GlobalOrchestrator&lt;&#x2F;code&gt;.
After, they will need to launch the training on each node manually. Extra configuration is also required for the nodes to find each other.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; collective&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; CollectiveConfig&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;with_global_address&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Address&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;from_str&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ws:&#x2F;&#x2F;example.com&#x2F;orchestrator&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;with_num_nodes&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;with_node_address&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Address&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;from_str&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;ws:&#x2F;&#x2F;example.com&#x2F;node_1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;with_data_service_port&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;3000&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; learner&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; LearnerBuilder&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;ARTIFACT_DIR&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;learning_strategy&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;burn&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;train&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;ddp&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;vec!&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;gpu_1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gpu_2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gpu_3&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; collective&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;build&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;optimizer&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;init&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;e&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;4&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;With burn-collective and the new DDP learning strategy, training on multiple GPUs or even across multiple nodes is now straightforward in Burn. On a single machine, users only need to provide their devices—the framework handles threading and gradient synchronization automatically. Scaling to multiple nodes requires some extra configuration for the orchestrator and addresses, but the API stays consistent, and the communication layer abstracts away the complexity.&lt;&#x2F;p&gt;
&lt;p&gt;The key point is that you don’t need to learn NCCL, MPI, or low-level communication details. Burn provides a unified interface for collective operations that works across devices and nodes, while still letting you choose strategies that match your hardware. This makes it possible to start small and scale up without rewriting your training code.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re already training models in Burn, upgrading to distributed data parallel training is just a few lines of code away.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Chickadee - Game Engine</title>
        <published>2025-01-01T00:00:00+00:00</published>
        <updated>2025-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/projects/chickadee/"/>
        <id>https://cielbird.github.io/projects/chickadee/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/projects/chickadee/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>Geþeode - Phonetics Engine</title>
        <published>2025-01-01T00:00:00+00:00</published>
        <updated>2025-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/projects/getheode/"/>
        <id>https://cielbird.github.io/projects/getheode/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/projects/getheode/">&lt;h2 id=&quot;blogs&quot;&gt;Blogs&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;cielbird.github.io&#x2F;blog&#x2F;modeling-phonology&#x2F;&quot;&gt;Modeling phonology&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;example-demo&quot;&gt;Example demo&lt;&#x2F;h2&gt;
&lt;p&gt;This demo showcases the &lt;code&gt;getheode apply&lt;&#x2F;code&gt; tool.&lt;&#x2F;p&gt;
&lt;div class=&quot;wasm-demo-container&quot;&gt;
  &lt;div class=&quot;wasm-demo-header&quot;&gt;
    &lt;span class=&quot;wasm-badge&quot;&gt;WASM&lt;&#x2F;span&gt;
    &lt;span&gt;Geþeode Apply Demo&lt;&#x2F;span&gt;
    &lt;a href=&quot;&amp;#x2F;demos&amp;#x2F;getheode-demo&amp;#x2F;&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; class=&quot;wasm-fullscreen&quot;&gt;Open fullscreen ↗&lt;&#x2F;a&gt;
  &lt;&#x2F;div&gt;
  &lt;iframe
    src=&quot;&amp;#x2F;demos&amp;#x2F;getheode-demo&amp;#x2F;&quot;
    width=&quot;100%&quot;
    height=&quot;120&quot;
    class=&quot;wasm-iframe&quot;
    title=&quot;Geþeode Apply Demo&quot;
    loading=&quot;lazy&quot;
    allow=&quot;fullscreen&quot;
    frameborder=&quot;0&quot;
  &gt;&lt;&#x2F;iframe&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;syntax&quot;&gt;Syntax&lt;&#x2F;h3&gt;
&lt;p&gt;Rules follow the notation &lt;code&gt;X → Y &#x2F; A _ B&lt;&#x2F;code&gt;: change X to Y when preceded by A and followed by B.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Example&lt;&#x2F;th&gt;&lt;th&gt;Meaning&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;X -&amp;gt; Y&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;change X to Y unconditionally&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;X -&amp;gt; Y &#x2F; A _ B&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;change X to Y when preceded by A, followed by B&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;_&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;position of the target in the environment&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;#&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;word boundary&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;$&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;syllable boundary&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;V&lt;&#x2F;code&gt;, &lt;code&gt;C&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;natural classes — vowel, consonant&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;[+voi]&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;feature set (any combination of &lt;code&gt;±&lt;&#x2F;code&gt; features)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;(X)&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;X is optional&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;{a,b,c}&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;one of a, b, or c&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;∅&lt;&#x2F;code&gt; or &lt;code&gt;Ø&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;empty segment (deletion or insertion)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;A B → X Y&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;multiple simultaneous rules&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;examples&quot;&gt;Examples&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rule&lt;&#x2F;th&gt;&lt;th&gt;Input&lt;&#x2F;th&gt;&lt;th&gt;Output&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;t → d &#x2F; V_V&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;ata&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;ada&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;intervocalic voicing&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ə → ∅ &#x2F; _#&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;taɪmə&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;taɪm&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;word-final schwa deletion&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;θ ð → t d&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;ði.θo&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;di.to&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;multiple rules applied simultaneously&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;{n,q,h} → Ø &#x2F; _(s)&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;oqs.in.ihso&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;os.i.iso&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;deletion before optional s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;n → l &#x2F; #_(V){s,ʃ,h}V{m,b}#&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;niham&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;liham&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;word-initial context with alternatives&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;j → ∅ &#x2F; Ck_$&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;eskj.mo&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;esk.mo&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;deletion at syllable boundary&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Censoring clowns with FPGA</title>
        <published>2024-12-03T00:00:00+00:00</published>
        <updated>2025-10-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/blog/blurring-clowns/"/>
        <id>https://cielbird.github.io/blog/blurring-clowns/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/blog/blurring-clowns/">&lt;p&gt;In this blog post, I&#x27;ll talk about an interesting project I worked on, and what I would
have done differently.&lt;&#x2F;p&gt;
&lt;p&gt;I was working on a demonstration project about face detection and censoring.
Instead of designing a CNN&#x2F;YOLO model on FPGA (which would have been a crazy project on its own),
we used simple color detection.
The trick: the user would wear a red clown nose, which would be reliable to detect.
It was a sort of &#x27;clown censorship device&#x27;, funny and perfectly acceptable for the context.&lt;&#x2F;p&gt;
&lt;p&gt;At first, we used a 256x256 black rectangle to mask the area detected by our red color detection.&lt;&#x2F;p&gt;
&lt;p&gt;It was my job to implement a blur in the detection zone. This requires a convolutional filter,
with a blurring kernel.&lt;&#x2F;p&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;demo.485f65cd4abfac72.jpg&quot; alt=&quot;Demo&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;&lt;em&gt;This is what the clown censoring looked like. I had it configured to track a blue color,&lt;&#x2F;em&gt;
&lt;em&gt;so I was holding up a blue paper on my nose. It was very fun to play with!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;game-plan&quot;&gt;Game plan&lt;&#x2F;h1&gt;
&lt;p&gt;HDMI sweeps the image on the screen in rows: left to right, and top to bottom. So when displaying a
pixel, I don&#x27;t know what color the pixel to the right will be. So to apply a filter on an image, I
needed to use some previous frame as input. This meant I needed to store the entire region in
memory.&lt;&#x2F;p&gt;
&lt;p&gt;My first game plan was as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;In one frame, store the face&#x27;s image.&lt;&#x2F;li&gt;
&lt;li&gt;On the next frame, display the previous frame&#x27;s filtered image in the new detection zone.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;downsampling&quot;&gt;Downsampling&lt;&#x2F;h2&gt;
&lt;p&gt;The first issue I ran into was storing the detected zone in registers. I had access to the pixel
colors in RGB888 format, which is 3 bytes per pixel. So I needed
$256 \times 256 \times 24 = 1572864$ flip-flops. That was way too much for the simple
Zybo Z7-20 FPGA dev-board I was using.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to downsample the input by a factor of 16; from 256x256 to 16x16.
This inherently added some pixelization to the filter, which was fine.&lt;&#x2F;p&gt;
&lt;p&gt;For this crude application, I didn&#x27;t do any anti-aliasing before downsampling.&lt;&#x2F;p&gt;
&lt;p&gt;I used a 3x3 kernel to apply the filter:&lt;&#x2F;p&gt;
&lt;p&gt;$$
O(x, y) = \sum_{i=-1}^{1} \sum_{j=-1}^{1} I(x+i, y+j) \times K(i, j)
$$&lt;&#x2F;p&gt;
&lt;p&gt;The beautiful thing about FPGA is that all these operations can easily be in parallel.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;timing&quot;&gt;Timing&lt;&#x2F;h2&gt;
&lt;p&gt;The second issue was timing. The convolution calculations are hefty, and
I needed this done in only a couple clock cycles. This caused timing issues, and
I struggled with the screen going black. This is what my code somewhat looked like at this stage:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;vhdl&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;architecture&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; behavioral&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; of&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; conv3x3&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; is&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;    -- 3x3 kernel as float constants&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K00 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K01 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K02 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K10 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K11 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;4.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K12 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K20 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K21 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    constant&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; K22 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32 &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; to_float&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;16.0&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;#39;high&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    signal&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; sum &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; float32&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;begin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    process&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;all&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    begin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;        sum &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;:=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p00&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K00&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p01&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K01&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p02&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K02&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;               (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p10&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K10&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p11&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K11&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p12&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K12&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;               (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p20&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K20&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p21&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K21&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;p22&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;K22&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;        result_out &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;&amp;lt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; sum&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    end&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; process&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;end&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; architecture&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There were two issues here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I wasn&#x27;t pipelining my operations&lt;&#x2F;li&gt;
&lt;li&gt;I was using floating point arithmetic&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I added a 5 stage pipeline that helped divide the operations into smaller more manageable chunks.
Now I could assure the whole calculation would only take 5 clock cycles.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot;&gt;&lt;code data-lang=&quot;vhdl&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt; FSM_STATE &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;is&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;IDLE&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; MULT&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ADD_1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ADD_2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ADD_3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#005CC5, #79B8FF);&quot;&gt; OUTPUT&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;pipeline.c548ed6ab564f42b.png&quot; alt=&quot;5 stage pipeline&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;Second, I used integers instead of floats in the calculations.
Using integers, I could simply multiply by 1, 2, and 4, and on the output step simply
divide by 16 using a right-bit-shift. Using integers allowed me to go much faster, and I saw a
noticeable drop in the instability of the design.&lt;&#x2F;p&gt;
&lt;p&gt;In the end, we had an architecture that looked something like this:&lt;&#x2F;p&gt;

&lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;cielbird.github.io&amp;#x2F;processed_images&amp;#x2F;full_diagram.9de1ae3efbb67b15.png&quot; alt=&quot;Architecture&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;Ultimately, this was a very fun project. Convolutions are everywhere, and opitimizing the calculation for a real-world application is pretty cool. The biggest mistake I made was ignoring BRAM. If I had used BRAM to
store the input image, I could have avoided downsampling and taken advantage of the
&amp;gt;600 KB of available memory on the Zybo Z7-20 FPGA dev-board.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Shattered</title>
        <published>2021-06-11T00:00:00+00:00</published>
        <updated>2021-06-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/projects/shattered/"/>
        <id>https://cielbird.github.io/projects/shattered/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/projects/shattered/">&lt;iframe frameBorder=&quot;0&quot; src=&quot;https:&#x2F;&#x2F;itch.io&#x2F;embed&#x2F;1083181?linkback=true&quot; width=&quot;552&quot; height=&quot;167&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;etn-25.itch.io&#x2F;shattered&quot;&gt;Shattered by ETN, Kuroyasha, Miya Loustalot, YaBoiCielbird, Valentin Coubronne, Nathan Mercier&lt;&#x2F;a&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Developed in a team of 6 for the GMTK 2021 game jam. Theme: &lt;em&gt;&quot;Joined Together&quot;&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ranked #179 overall out of 5,600 submissions top 3%. Built in Unity 3D using C# in 48 hours with all original assets.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Out Of Controls</title>
        <published>2020-06-12T00:00:00+00:00</published>
        <updated>2020-06-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/projects/out-of-controls/"/>
        <id>https://cielbird.github.io/projects/out-of-controls/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/projects/out-of-controls/">&lt;iframe frameBorder=&quot;0&quot; src=&quot;https:&#x2F;&#x2F;itch.io&#x2F;embed&#x2F;697773?linkback=true&quot; width=&quot;552&quot; height=&quot;167&quot;&gt;&lt;a href=&quot;https:&#x2F;&#x2F;yaboicielbird.itch.io&#x2F;out-of-controls&quot;&gt;Out Of Controls by YaBoiCielbird, TKDR&lt;&#x2F;a&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Developed with a friend for the GMTK 2020 game jam. Theme: &lt;em&gt;&quot;Out of Control&quot;&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Built in Unity 3D using C#, in 48 hours with all original assets.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Outlander&#x27;s Inn</title>
        <published>2020-01-01T00:00:00+00:00</published>
        <updated>2020-01-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/projects/outlanders-inn/"/>
        <id>https://cielbird.github.io/projects/outlanders-inn/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/projects/outlanders-inn/"></content>
        
    </entry>
    <entry xml:lang="en">
        <title>Voxel Planet Demo</title>
        <published>2019-08-20T00:00:00+00:00</published>
        <updated>2019-08-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cielbird.github.io/projects/voxel-planet/"/>
        <id>https://cielbird.github.io/projects/voxel-planet/</id>
        
        <content type="html" xml:base="https://cielbird.github.io/projects/voxel-planet/"></content>
        
    </entry>
</feed>
