&lt;?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Articles from July 2026 on PowerShell.org - Welcome Automaters!</title><link>https://powershell.org/articles/2026/07/</link><description>Recent content in Articles from July 2026 on PowerShell.org - Welcome Automaters!</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://powershell.org/articles/2026/07/index.xml" rel="self" type="application/rss+xml"/><item><title>Explore Micrograd with Verso and PowerShell</title><link>https://powershell.org/articles/2026-07-24-explore-micrograd-with-verso-and-powershell/</link><guid>https://powershell.org/articles/2026-07-24-explore-micrograd-with-verso-and-powershell/</guid><pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate><description>&lt;p&gt;&lt;a href="https://github.com/DataficationSDK/Verso"&gt;Verso&lt;/a&gt; is an open-source interactive
notebook platform and embeddable .NET execution engine. Its language kernels include
PowerShell, C#, F#, Python, SQL, JavaScript, TypeScript, and HTTP, and it provides
VS Code and browser front ends.&lt;/p&gt;
&lt;p&gt;That timing is useful for PowerShell users. The
&lt;a href="https://github.com/dotnet/interactive"&gt;.NET Interactive repository&lt;/a&gt; was archived in
April 2026, leaving a gap for maintained multi-language .NET notebooks. Verso is an
actively developed option with persistent kernel state, rich output, cross-language
variable sharing, and headless notebook execution.&lt;/p&gt;</description><content:encoded>&lt;![CDATA[<p><a href="https://github.com/DataficationSDK/Verso">Verso</a> is an open-source interactive
notebook platform and embeddable .NET execution engine. Its language kernels include
PowerShell, C#, F#, Python, SQL, JavaScript, TypeScript, and HTTP, and it provides
VS Code and browser front ends.</p><p>That timing is useful for PowerShell users. The<a href="https://github.com/dotnet/interactive">.NET Interactive repository</a> was archived in
April 2026, leaving a gap for maintained multi-language .NET notebooks. Verso is an
actively developed option with persistent kernel state, rich output, cross-language
variable sharing, and headless notebook execution.</p><p>This post walks through an<a href="https://github.com/DataficationSDK/Verso/blob/3f8629154a28824ad5fbd0eaca49c3ef57168704/samples/Notebooks/powershell/micrograd/micrograd-ps.verso">experimental PowerShell micrograd notebook</a>
built for Verso. The sample was proposed separately and is not currently part of
Verso&rsquo;s<code>main</code> branch, so treat it as an exploration rather than a shipped Verso
sample.</p><p>The notebook ports the core ideas from Andrej Karpathy&rsquo;s<a href="https://github.com/karpathy/micrograd">micrograd</a> to PowerShell. The original
project is intentionally tiny: scalar-valued reverse-mode automatic differentiation,
then a small neural-network library on top. Karpathy&rsquo;s video,<a href="https://youtu.be/VMj-3S1tku0">The spelled-out intro to neural networks and backpropagation: building micrograd</a>,
is effective because it does not hide the graph. The PowerShell version keeps that
spirit, using<a href="https://github.com/eosfor/PSGraph">PSQuickGraph</a> and<a href="https://github.com/eosfor/PSGraphView/tree/feature/direct-graphviz-integration">PSGraphView</a>
to render the computation graph directly from objects created in the notebook.</p><p>The diagrams in this article were generated by PowerShell from the same helper
scripts used by the notebook and exported as Graphviz SVG through PSGraphView.</p><h2 id="notebook-setup" class="ps-heading">Notebook Setup<a class="ps-heading-anchor" href="#notebook-setup" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>Install the Verso CLI and the two graph modules:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>dotnet tool install --global Verso.Cli</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>Install-Module -Name PSQuickGraph -RequiredVersion<span style="color:#a5d6ff">2.5</span>.<span style="color:#79c0ff">0</span> -Scope CurrentUser</span></span><span style="display:flex;"><span>Install-Module -Name PSGraphView -RequiredVersion<span style="color:#a5d6ff">0.1</span>.<span style="color:#79c0ff">0</span> -Scope CurrentUser</span></span></code></pre></td></tr></table></div></div><p>To open the exact experimental notebook used in this article, check out its commit
and pass the notebook path to Verso:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">5</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>git clone https<span style="color:#f85149">:</span>//github.com/DataficationSDK/Verso.<span style="color:#79c0ff">git</span></span></span><span style="display:flex;"><span>Set-Location ./Verso</span></span><span style="display:flex;"><span>git checkout 3f8629154a28824ad5fbd0eaca49c3ef57168704</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>verso serve ./samples/Notebooks/powershell/micrograd/micrograd-ps.<span style="color:#79c0ff">verso</span></span></span></code></pre></td></tr></table></div></div><p>The notebook starts with the normal module path:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>Import-Module PSQuickGraph</span></span><span style="display:flex;"><span>Import-Module PSGraphView</span></span></code></pre></td></tr></table></div></div><p>The implementation is split into four scripts:</p><ul><li><code>value.ps1</code> defines the scalar<code>Value</code> class and operator overloads.</li><li><code>graphHelper.ps1</code> converts<code>Value</code> objects into graph vertices and renders them.</li><li><code>neuronHelper.ps1</code> defines<code>Neuron</code>,<code>Layer</code>, and<code>MLP</code>.</li><li><code>helpers.ps1</code> contains<code>Zip</code> and<code>Sum-Value</code>, small utilities used when building the loss.</li></ul><p>The notebook loads them directly:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>. ./value.<span style="color:#79c0ff">ps1</span></span></span><span style="display:flex;"><span>. ./graphHelper.<span style="color:#79c0ff">ps1</span></span></span><span style="display:flex;"><span>. ./neuronHelper.<span style="color:#79c0ff">ps1</span></span></span><span style="display:flex;"><span>. ./helpers.<span style="color:#79c0ff">ps1</span></span></span></code></pre></td></tr></table></div></div><p>The key class is<code>Value</code>. Each instance stores<code>data</code>,<code>grad</code>, a<code>label</code>, the operation that produced it, the child values that fed into that operation, and a<code>backward</code> closure. That is the entire trick: normal arithmetic produces both a result and a tiny piece of local derivative logic.</p><p>For addition, the derivative is one for both inputs:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>static [<span style="color:#79c0ff;font-weight:bold">Value</span>] op_Addition([<span style="color:#79c0ff;font-weight:bold">Value</span>]<span style="color:#79c0ff">$left</span>, [<span style="color:#79c0ff;font-weight:bold">Value</span>]<span style="color:#79c0ff">$right</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$out</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#79c0ff">$left</span>.<span style="color:#79c0ff">data</span> +<span style="color:#79c0ff">$right</span>.data,<span style="color:#79c0ff">@</span>(<span style="color:#79c0ff">$left</span>,<span style="color:#79c0ff">$right</span>),<span style="color:#a5d6ff">"+"</span>,<span style="color:#a5d6ff">"+_res"</span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">backward</span> = {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$left</span>.<span style="color:#79c0ff">grad</span> +=<span style="color:#a5d6ff">1</span> *<span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">grad</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$right</span>.<span style="color:#79c0ff">grad</span> +=<span style="color:#a5d6ff">1</span> *<span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">grad</span></span></span><span style="display:flex;"><span> }.GetNewClosure()</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">return</span><span style="color:#79c0ff">$out</span></span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p>For multiplication, each input receives the other input&rsquo;s data multiplied by the output gradient:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>static [<span style="color:#79c0ff;font-weight:bold">Value</span>] op_Multiply([<span style="color:#79c0ff;font-weight:bold">Value</span>]<span style="color:#79c0ff">$left</span>, [<span style="color:#79c0ff;font-weight:bold">Value</span>]<span style="color:#79c0ff">$right</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$out</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#79c0ff">$left</span>.<span style="color:#79c0ff">data</span> *<span style="color:#79c0ff">$right</span>.data,<span style="color:#79c0ff">@</span>(<span style="color:#79c0ff">$left</span>,<span style="color:#79c0ff">$right</span>),<span style="color:#a5d6ff">"*"</span>,<span style="color:#a5d6ff">"*_res"</span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">backward</span> = {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$left</span>.<span style="color:#79c0ff">grad</span> +=<span style="color:#79c0ff">$right</span>.<span style="color:#79c0ff">data</span> *<span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">grad</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$right</span>.<span style="color:#79c0ff">grad</span> +=<span style="color:#79c0ff">$left</span>.<span style="color:#79c0ff">data</span> *<span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">grad</span></span></span><span style="display:flex;"><span> }.GetNewClosure()</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">return</span><span style="color:#79c0ff">$out</span></span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p><code>Tanh()</code> follows the same pattern, but the derivative is<code>1 - tanh(x)^2</code>:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>[<span style="color:#79c0ff;font-weight:bold">Value</span>] Tanh(){</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$v</span> =<span style="color:#79c0ff">$this</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$t</span> = [<span style="color:#79c0ff;font-weight:bold">Math</span>]::Tanh(<span style="color:#79c0ff">$this</span>.data)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$out</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#79c0ff">$t</span>,<span style="color:#79c0ff">@</span>(<span style="color:#79c0ff">$this</span>),<span style="color:#a5d6ff">"tanh"</span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">backward</span> = {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$v</span>.<span style="color:#79c0ff">grad</span> += (<span style="color:#a5d6ff">1</span> - [<span style="color:#79c0ff;font-weight:bold">Math</span>]::Pow(<span style="color:#79c0ff">$t</span>,<span style="color:#a5d6ff">2</span>)) *<span style="color:#79c0ff">$out</span>.<span style="color:#79c0ff">grad</span></span></span><span style="display:flex;"><span> }.GetNewClosure()</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">return</span><span style="color:#79c0ff">$out</span></span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><h2 id="scalar-computation-graph" class="ps-heading">Scalar Computation Graph<a class="ps-heading-anchor" href="#scalar-computation-graph" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>The first notebook example is the same kind of scalar expression Karpathy uses to make backpropagation visible:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">7</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$a</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">2.0</span>,<span style="color:#a5d6ff">'a'</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$b</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-3.0</span>,<span style="color:#a5d6ff">'b'</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$c</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">10.0</span>,<span style="color:#a5d6ff">'c'</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$e</span> =<span style="color:#79c0ff">$a</span> *<span style="color:#79c0ff">$b</span>;<span style="color:#79c0ff">$e</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'e'</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$d</span> =<span style="color:#79c0ff">$e</span> +<span style="color:#79c0ff">$c</span>;<span style="color:#79c0ff">$d</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'d'</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$f</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-2.0</span>,<span style="color:#a5d6ff">'f'</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$L</span> =<span style="color:#79c0ff">$d</span> *<span style="color:#79c0ff">$f</span>;<span style="color:#79c0ff">$L</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'L'</span></span></span></code></pre></td></tr></table></div></div><p>At this point<code>$L.data</code> is<code>-8</code>, and all gradients are still zero. The graph is created from the output value:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$scalarGraph</span> = New-ExpressionGraph -val<span style="color:#79c0ff">$L</span></span></span><span style="display:flex;"><span>Show-ExpressionGraph -Graph<span style="color:#79c0ff">$scalarGraph</span></span></span></code></pre></td></tr></table></div></div><p><img src="/images/articles/verso-micrograd-scalar-before.svg" alt="Scalar expression before backpropagation; data values are populated and every gradient is zero"/><p><code>New-ExpressionGraph</code> walks from the output node back through<code>children</code>. It creates record-shaped nodes for values and ellipse-shaped nodes for operations. Because<code>Value</code> objects are actual object references, helper hashtables prevent duplicate vertices when a value is reached more than once.</p><h2 id="backpropagation-order" class="ps-heading">Backpropagation Order<a class="ps-heading-anchor" href="#backpropagation-order" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>Backpropagation is not run over the display graph. The notebook builds a second graph directly on the original<code>Value</code> objects:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">6</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$bpGraph</span> = New-BackpropagationGraph -val<span style="color:#79c0ff">$L</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$L</span>.<span style="color:#79c0ff">grad</span> =<span style="color:#a5d6ff">1.0</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>Get-GraphTopologicalSort -Graph<span style="color:#79c0ff">$bpGraph</span> -Reverse |</span></span><span style="display:flex;"><span> ForEach-Object {<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">OriginalObject</span> } |</span></span><span style="display:flex;"><span> ForEach-Object { &amp;<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">backward</span> }</span></span></code></pre></td></tr></table></div></div><p>The output gradient starts at<code>1.0</code>, because<code>dL/dL = 1</code>. Then<code>Get-GraphTopologicalSort -Reverse</code> visits the output first and walks backward toward the leaves. Each node executes the closure captured when the value was created. After the pass, the visualization graph is rebuilt so the display nodes get a fresh snapshot of<code>grad</code>.</p><p><img src="/images/articles/verso-micrograd-scalar-after.svg" alt="The scalar expression after backpropagation; gradients show how each input changes L"/><p>This is the important implementation detail: the graph is not just a drawing. It is the execution dependency structure for reverse-mode autodiff.</p><h2 id="one-neuron" class="ps-heading">One Neuron<a class="ps-heading-anchor" href="#one-neuron" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>The next cell builds a tiny neuron by hand: two inputs, two weights, a bias, and a<code>tanh</code> activation.</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">12</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$x1</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">2.0</span>,<span style="color:#a5d6ff">'x1'</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$x2</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">0.0</span>,<span style="color:#a5d6ff">'x2'</span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$w1</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-3.0</span>,<span style="color:#a5d6ff">'w1'</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$w2</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">1.0</span>,<span style="color:#a5d6ff">'w2'</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$b</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">6.8813735870195432</span>,<span style="color:#a5d6ff">'b'</span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$x1w1</span> =<span style="color:#79c0ff">$x1</span> *<span style="color:#79c0ff">$w1</span>;<span style="color:#79c0ff">$x1w1</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'x1*w1'</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$x2w2</span> =<span style="color:#79c0ff">$x2</span> *<span style="color:#79c0ff">$w2</span>;<span style="color:#79c0ff">$x2w2</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'x2*w2'</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$x1w1x2w2</span> =<span style="color:#79c0ff">$x1w1</span> +<span style="color:#79c0ff">$x2w2</span>;<span style="color:#79c0ff">$x1w1x2w2</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'x1*w1 + x2*w2'</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$n</span> =<span style="color:#79c0ff">$x1w1x2w2</span> +<span style="color:#79c0ff">$b</span>;<span style="color:#79c0ff">$n</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'n'</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$o</span> =<span style="color:#79c0ff">$n</span>.<span style="color:#79c0ff">Tanh</span>();<span style="color:#79c0ff">$o</span>.<span style="color:#79c0ff">label</span> =<span style="color:#a5d6ff">'o'</span></span></span></code></pre></td></tr></table></div></div><p><img src="/images/articles/verso-micrograd-neuron-before.svg" alt="A single tanh neuron before the backward pass"/><p>Running the same topological backward pass from<code>$o</code> fills the gradients for the input, weights, bias, and intermediate values:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">6</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$bpNeuronGraph</span> = New-BackpropagationGraph -val<span style="color:#79c0ff">$o</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$o</span>.<span style="color:#79c0ff">grad</span> =<span style="color:#a5d6ff">1.0</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>Get-GraphTopologicalSort -Graph<span style="color:#79c0ff">$bpNeuronGraph</span> -Reverse |</span></span><span style="display:flex;"><span> ForEach-Object {<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">OriginalObject</span> } |</span></span><span style="display:flex;"><span> ForEach-Object { &amp;<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">backward</span> }</span></span></code></pre></td></tr></table></div></div><p><img src="/images/articles/verso-micrograd-neuron-after.svg" alt="The neuron after the tanh derivative has propagated through additions and multiplications"/><p>This is where the notebook starts to feel useful as a teaching tool. You can inspect every scalar contribution to the neuron instead of treating the neuron as a black box.</p><h2 id="layer-and-mlp" class="ps-heading">Layer and MLP<a class="ps-heading-anchor" href="#layer-and-mlp" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>After the manual neuron,<code>neuronHelper.ps1</code> turns the same logic into classes. A<code>Neuron</code> owns an array of weights and a bias:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">12</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">13</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">14</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">15</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">16</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">17</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">18</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">19</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">20</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">21</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#ff7b72">class</span><span style="color:#6e7681"/>Neuron {</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value[]</span>]<span style="color:#79c0ff">$w</span></span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]<span style="color:#79c0ff">$b</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span> Neuron([<span style="color:#79c0ff;font-weight:bold">int</span>]<span style="color:#79c0ff">$nin</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$this</span>.<span style="color:#79c0ff">w</span> =<span style="color:#ff7b72">for</span> (<span style="color:#79c0ff">$i</span> =<span style="color:#a5d6ff">0</span>;<span style="color:#79c0ff">$i</span><span style="color:#ff7b72;font-weight:bold">-lt</span><span style="color:#79c0ff">$nin</span>;<span style="color:#79c0ff">$i</span>++) {</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(([<span style="color:#79c0ff;font-weight:bold">Random</span>]::Shared.<span style="color:#79c0ff">NextDouble</span>() *<span style="color:#a5d6ff">2</span> -<span style="color:#a5d6ff">1</span>),<span style="color:#a5d6ff">"w</span><span style="color:#79c0ff">$i</span><span style="color:#a5d6ff">"</span>)</span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$this</span>.<span style="color:#79c0ff">b</span> = [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(([<span style="color:#79c0ff;font-weight:bold">Random</span>]::Shared.<span style="color:#79c0ff">NextDouble</span>() *<span style="color:#a5d6ff">2</span> -<span style="color:#a5d6ff">1</span>),<span style="color:#a5d6ff">"b"</span>)</span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>] Invoke([<span style="color:#79c0ff;font-weight:bold">Value[]</span>]<span style="color:#79c0ff">$x</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$sum</span> =<span style="color:#79c0ff">$this</span>.<span style="color:#79c0ff">b</span></span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> (<span style="color:#79c0ff">$i</span> =<span style="color:#a5d6ff">0</span>;<span style="color:#79c0ff">$i</span><span style="color:#ff7b72;font-weight:bold">-lt</span><span style="color:#79c0ff">$this</span>.<span style="color:#79c0ff">w</span>.Count;<span style="color:#79c0ff">$i</span>++) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$sum</span> =<span style="color:#79c0ff">$sum</span> + (<span style="color:#79c0ff">$this</span>.w[<span style="color:#79c0ff">$i</span>] *<span style="color:#79c0ff">$x</span>[<span style="color:#79c0ff">$i</span>])</span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">return</span><span style="color:#79c0ff">$sum</span>.<span style="color:#79c0ff">Tanh</span>()</span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p>A<code>Layer</code> applies several neurons to the same input vector. An<code>MLP</code> chains layers so each layer receives the output vector from the previous layer:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">12</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$x</span> =<span style="color:#79c0ff">@</span>(</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">2.0</span>,<span style="color:#a5d6ff">'x1'</span>)</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">3.0</span>,<span style="color:#a5d6ff">'x2'</span>)</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-1.0</span>,<span style="color:#a5d6ff">'x3'</span>)</span></span><span style="display:flex;"><span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$layer</span> = [<span style="color:#79c0ff;font-weight:bold">Layer</span>]::new(<span style="color:#a5d6ff">3</span>,<span style="color:#a5d6ff">4</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$layer</span>.<span style="color:#79c0ff">Invoke</span>(<span style="color:#79c0ff">$x</span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$net</span> = [<span style="color:#79c0ff;font-weight:bold">MLP</span>]::new(<span style="color:#a5d6ff">3</span>,<span style="color:#79c0ff">@</span>(<span style="color:#a5d6ff">4</span>,<span style="color:#a5d6ff">4</span>,<span style="color:#a5d6ff">1</span>))</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$res</span> =<span style="color:#79c0ff">$net</span>.<span style="color:#79c0ff">Invoke</span>(<span style="color:#79c0ff">$x</span>)</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$res</span></span></span></code></pre></td></tr></table></div></div><p>The notebook can render the full MLP expression graph too:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$netGraph</span> = New-ExpressionGraph -val<span style="color:#79c0ff">$res</span>[<span style="color:#a5d6ff">0</span>]</span></span><span style="display:flex;"><span>Show-ExpressionGraph -Graph<span style="color:#79c0ff">$netGraph</span> -rankdir<span style="color:#a5d6ff">'TD'</span></span></span></code></pre></td></tr></table></div></div><p>That graph is intentionally not embedded here: it is already wide enough to be less readable in a blog post. The smaller scalar and neuron graphs make the mechanics clearer.</p><h2 id="training-data-and-loss" class="ps-heading">Training Data and Loss<a class="ps-heading-anchor" href="#training-data-and-loss" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>The training set is the small toy dataset from the micrograd walkthrough:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">12</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">13</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$xs</span> =<span style="color:#79c0ff">@</span>(</span></span><span style="display:flex;"><span><span style="color:#79c0ff">@</span>([<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">2.0</span>,<span style="color:#a5d6ff">'x11'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">3.0</span>,<span style="color:#a5d6ff">'x12'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-1.0</span>,<span style="color:#a5d6ff">'x13'</span>)),</span></span><span style="display:flex;"><span><span style="color:#79c0ff">@</span>([<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">3.0</span>,<span style="color:#a5d6ff">'x21'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-1.0</span>,<span style="color:#a5d6ff">'x22'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">0.5</span>,<span style="color:#a5d6ff">'x23'</span>)),</span></span><span style="display:flex;"><span><span style="color:#79c0ff">@</span>([<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">0.5</span>,<span style="color:#a5d6ff">'x31'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">1.0</span>,<span style="color:#a5d6ff">'x32'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">1.0</span>,<span style="color:#a5d6ff">'x33'</span>)),</span></span><span style="display:flex;"><span><span style="color:#79c0ff">@</span>([<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">1.0</span>,<span style="color:#a5d6ff">'x41'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">1.0</span>,<span style="color:#a5d6ff">'x42'</span>), [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-1.0</span>,<span style="color:#a5d6ff">'x43'</span>))</span></span><span style="display:flex;"><span>)</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$ys</span> =<span style="color:#79c0ff">@</span>(</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">1.0</span>,<span style="color:#a5d6ff">'y1'</span>),</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-1.0</span>,<span style="color:#a5d6ff">'y2'</span>),</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">-1.0</span>,<span style="color:#a5d6ff">'y3'</span>),</span></span><span style="display:flex;"><span> [<span style="color:#79c0ff;font-weight:bold">Value</span>]::new(<span style="color:#a5d6ff">1.0</span>,<span style="color:#a5d6ff">'y4'</span>)</span></span><span style="display:flex;"><span>)</span></span></code></pre></td></tr></table></div></div><p>The loss is sum of squared errors:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">7</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$net</span> = [<span style="color:#79c0ff;font-weight:bold">MLP</span>]::new(<span style="color:#a5d6ff">3</span>,<span style="color:#79c0ff">@</span>(<span style="color:#a5d6ff">4</span>,<span style="color:#a5d6ff">4</span>,<span style="color:#a5d6ff">1</span>))</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$ypred</span> =<span style="color:#79c0ff">$xs</span> | ForEach-Object {<span style="color:#79c0ff">$net</span>.<span style="color:#79c0ff">Invoke</span>(<span style="color:#79c0ff">$_</span>)[<span style="color:#a5d6ff">0</span>] }</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$loss</span> = Zip -Left<span style="color:#79c0ff">$ys</span> -Right<span style="color:#79c0ff">$ypred</span> | Sum-Value {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$diff</span> =<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">Right</span> -<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">Left</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$diff</span> *<span style="color:#79c0ff">$diff</span></span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p><code>Zip</code> pairs expected and predicted values.<code>Sum-Value</code> starts from a<code>Value</code> named<code>loss</code> and keeps adding selected terms. Because every subtraction, multiplication, and addition returns another<code>Value</code>, the loss is also a scalar root of a full computation graph.</p><h2 id="one-training-step" class="ps-heading">One Training Step<a class="ps-heading-anchor" href="#one-training-step" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>One optimization step follows the same shape as PyTorch, but without hiding anything:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">12</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">13</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">14</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">15</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">16</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">17</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">18</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">19</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">20</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">21</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">22</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">23</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">24</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">25</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">26</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$p</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$net</span>.<span style="color:#79c0ff">parameters</span>()) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$p</span>.<span style="color:#79c0ff">grad</span> =<span style="color:#a5d6ff">0.0</span></span></span><span style="display:flex;"><span>}</span></span><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$row</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$xs</span>) {</span></span><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$v</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$row</span>) {<span style="color:#79c0ff">$v</span>.<span style="color:#79c0ff">grad</span> =<span style="color:#a5d6ff">0.0</span> }</span></span><span style="display:flex;"><span>}</span></span><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$y</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$ys</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$y</span>.<span style="color:#79c0ff">grad</span> =<span style="color:#a5d6ff">0.0</span></span></span><span style="display:flex;"><span>}</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$ypred</span> =<span style="color:#79c0ff">$xs</span> | ForEach-Object {<span style="color:#79c0ff">$net</span>.<span style="color:#79c0ff">Invoke</span>(<span style="color:#79c0ff">$_</span>)[<span style="color:#a5d6ff">0</span>] }</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$loss</span> = Zip -Left<span style="color:#79c0ff">$ys</span> -Right<span style="color:#79c0ff">$ypred</span> | Sum-Value {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$diff</span> =<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">Right</span> -<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">Left</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$diff</span> *<span style="color:#79c0ff">$diff</span></span></span><span style="display:flex;"><span>}</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$loss</span>.<span style="color:#79c0ff">grad</span> =<span style="color:#a5d6ff">1.0</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$bpLossGraph</span> = New-BackpropagationGraph -val<span style="color:#79c0ff">$loss</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>Get-GraphTopologicalSort -Graph<span style="color:#79c0ff">$bpLossGraph</span> -Reverse |</span></span><span style="display:flex;"><span> ForEach-Object {<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">OriginalObject</span> } |</span></span><span style="display:flex;"><span> ForEach-Object { &amp;<span style="color:#79c0ff">$_</span>.<span style="color:#79c0ff">backward</span> }</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$p</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$net</span>.<span style="color:#79c0ff">parameters</span>()) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$p</span>.<span style="color:#79c0ff">data</span> +=<span style="color:#a5d6ff">-0.1</span> *<span style="color:#79c0ff">$p</span>.<span style="color:#79c0ff">grad</span></span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p>There are five phases: clear gradients, forward pass, loss construction, backward pass, parameter update. The learning rate is hard-coded as<code>0.1</code> because this is a notebook demo, not a training framework.</p><h2 id="training-loop" class="ps-heading">Training Loop<a class="ps-heading-anchor" href="#training-loop" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>The notebook repeats that step 200 times. A shorter 80-epoch run shows the same
behavior: the sum of squared errors falls rapidly and then continues to converge.</p><p><img src="/images/articles/verso-micrograd-loss-history.svg" alt="Training loss over 80 epochs"/><p>The final notebook cell renders the full loss graph after training:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$lossGraph</span> = New-ExpressionGraph -val<span style="color:#79c0ff">$loss</span></span></span><span style="display:flex;"><span>Show-ExpressionGraph -Graph<span style="color:#79c0ff">$lossGraph</span> -rankdir<span style="color:#a5d6ff">'TD'</span></span></span></code></pre></td></tr></table></div></div><p>It is a useful stress test for<code>PSGraphView</code>, but it is too large for this page because it contains the complete scalar computation that produced the loss. That is also the point of micrograd: a neural network can be understood as a large scalar expression, and backpropagation is just the disciplined reverse walk over that expression.</p><h2 id="why-this-matters" class="ps-heading">Why This Matters<a class="ps-heading-anchor" href="#why-this-matters" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>The important part is not that PowerShell is the best language for building neural networks. It is not. The point is that Verso makes PowerShell notebooks feel real again after the end of .NET Interactive, and the PowerShell kernel can now do the things notebook users expect: long-running host output, cancellation, persistent state, rich display, and ordinary module-based workflows.</p><p>For infrastructure engineers, that matters. The same mechanics used here for micrograd graphs apply to dependency graphs, Azure topology, policy validation, incident analysis, and any other workflow where PowerShell produces structured objects and the notebook should make those objects visible.</p>
]]></content:encoded></item><item><title>Validate Azure Resource Relationships with PSRule and PowerShell Graphs</title><link>https://powershell.org/articles/2026-07-24-validate-azure-resource-relationships-with-psrule-and-powershell-graphs/</link><guid>https://powershell.org/articles/2026-07-24-validate-azure-resource-relationships-with-psrule-and-powershell-graphs/</guid><pubDate>Fri, 24 Jul 2026 00:00:00 +0000</pubDate><description>&lt;p&gt;PSRule for Azure makes it straightforward to validate the properties of individual
resources before deployment. But some architecture requirements describe a
relationship, not a property: every Function App must connect to the expected virtual
network, or every application must have exactly one private endpoint.&lt;/p&gt;
&lt;p&gt;This article shows how to collect those relationships in a PowerShell graph while
PSRule processes a Bicep deployment, then validate the completed graph at the end of
the pipeline.&lt;/p&gt;</description><content:encoded>&lt;![CDATA[<p>PSRule for Azure makes it straightforward to validate the properties of individual
resources before deployment. But some architecture requirements describe a
relationship, not a property: every Function App must connect to the expected virtual
network, or every application must have exactly one private endpoint.</p><p>This article shows how to collect those relationships in a PowerShell graph while
PSRule processes a Bicep deployment, then validate the completed graph at the end of
the pipeline.</p><h2 id="why-per-resource-rules-are-not-enough" class="ps-heading">Why per-resource rules are not enough<a class="ps-heading-anchor" href="#why-per-resource-rules-are-not-enough" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>Consider an architecture with a Function App, a virtual network, an integration
subnet, and a private endpoint subnet. We can easily write rules that check:</p><ul><li>whether the Function App has public access disabled;</li><li>whether the virtual network uses the correct address space;</li><li>whether the expected subnets exist.</li></ul><p>Those checks still do not prove that the Function App is connected to the correct
subnet or that its private endpoint belongs to the expected virtual network. The
required information is spread across several expanded ARM resources.</p><p>A graph is a natural representation of this problem:</p><ul><li>resources and subnets become vertices;</li><li>references between them become edges;</li><li>an architecture requirement becomes a path or edge-count assertion.</li></ul><h2 id="prepare-psrule-and-the-graph-module" class="ps-heading">Prepare PSRule and the graph module<a class="ps-heading-anchor" href="#prepare-psrule-and-the-graph-module" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>The example uses<a href="https://github.com/Azure/PSRule.Rules.Azure">PSRule.Rules.Azure</a> to expand and
analyze Bicep and<a href="https://github.com/eosfor/PSGraph">PSQuickGraph</a> to build the dependency graph.</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>Install-Module -Name PSRule.<span style="color:#79c0ff">Rules</span>.<span style="color:#79c0ff">Azure</span> -Scope CurrentUser</span></span><span style="display:flex;"><span>Install-Module -Name PSQuickGraph -Scope CurrentUser</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>Import-Module PSQuickGraph</span></span></code></pre></td></tr></table></div></div><p>In<code>ps-rule.yaml</code>, enable Bicep expansion and include the convention that will collect
relationships:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">include</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">module</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">PSRule.Rules.Azure</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">PSQuickGraph</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">convention</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">include</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/>-<span style="color:#a5d6ff">FullConnectivityTest</span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">configuration</span>:<span style="color:#6e7681"/></span></span><span style="display:flex;"><span><span style="color:#6e7681"/><span style="color:#7ee787">AZURE_BICEP_FILE_EXPANSION</span>:<span style="color:#6e7681"/><span style="color:#79c0ff">true</span><span style="color:#6e7681"/></span></span></code></pre></td></tr></table></div></div><h2 id="collect-relationships-with-a-convention" class="ps-heading">Collect relationships with a convention<a class="ps-heading-anchor" href="#collect-relationships-with-a-convention" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>A PSRule<a href="https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Conventions/">convention</a>
can run custom PowerShell at different stages of the pipeline. Its<code>Process</code> block
runs once for each input object, while its<code>End</code> block runs after all objects have
been processed.</p><p>That lifecycle gives us a convenient two-phase approach:</p><ol><li>Add every relevant resource and relationship to a graph.</li><li>Validate the graph only after the complete deployment has been seen.</li></ol><p>The following is a simplified version of the convention. The example uses
child-to-parent edges so a valid dependency chain ends at the virtual network.</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">11</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">12</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">13</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">14</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">15</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">16</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">17</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">18</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">19</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">20</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">21</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">22</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">23</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">24</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">25</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">26</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">27</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">28</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">29</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">30</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">31</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">32</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">33</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">34</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">35</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">36</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">37</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">38</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">39</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">40</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">41</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">42</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">43</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">44</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">45</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">46</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">47</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">48</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">49</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">50</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#79c0ff">$global:vnetId</span> =<span style="color:#79c0ff">$null</span></span></span><span style="display:flex;"><span><span style="color:#79c0ff">$global:webSites</span> =<span style="color:#79c0ff">@</span>()</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$global:connectionGraph</span> = New-Graph</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$global:privateEndpointGraph</span> = New-Graph</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>Export-PSRuleConvention<span style="color:#a5d6ff">'FullConnectivityTest'</span> -Process {</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> (<span style="color:#79c0ff">$TargetObject</span>.Type<span style="color:#ff7b72;font-weight:bold">-eq</span><span style="color:#a5d6ff">'Microsoft.Network/virtualNetworks'</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$global:vnetId</span> =<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Id</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$subnetResource</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$TargetObject</span>.Resources) {</span></span><span style="display:flex;"><span> Add-Edge `</span></span><span style="display:flex;"><span> -From<span style="color:#79c0ff">$subnetResource</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -To<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:connectionGraph</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span> Add-Edge `</span></span><span style="display:flex;"><span> -From<span style="color:#79c0ff">$subnetResource</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -To<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:privateEndpointGraph</span></span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> (<span style="color:#79c0ff">$TargetObject</span>.Type<span style="color:#ff7b72;font-weight:bold">-eq</span><span style="color:#a5d6ff">'Microsoft.Web/sites'</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$vnetIntegration</span> =<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Resources</span> |</span></span><span style="display:flex;"><span> Where-Object Type<span style="color:#ff7b72;font-weight:bold">-eq</span><span style="color:#a5d6ff">'Microsoft.Web/sites/networkConfig'</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#79c0ff">$global:webSites</span> +=<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Id</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span> Add-Edge `</span></span><span style="display:flex;"><span> -From<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -To<span style="color:#79c0ff">$vnetIntegration</span>.<span style="color:#79c0ff">Properties</span>.<span style="color:#79c0ff">SubnetResourceId</span> `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:connectionGraph</span></span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> (<span style="color:#79c0ff">$TargetObject</span>.Type<span style="color:#ff7b72;font-weight:bold">-eq</span><span style="color:#a5d6ff">'Microsoft.Network/privateEndpoints'</span>) {</span></span><span style="display:flex;"><span> Add-Edge `</span></span><span style="display:flex;"><span> -From<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -To<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Properties</span>.<span style="color:#79c0ff">Subnet</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:privateEndpointGraph</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$connection</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Properties</span>.PrivateLinkServiceConnections) {</span></span><span style="display:flex;"><span> Add-Edge `</span></span><span style="display:flex;"><span> -From<span style="color:#79c0ff">$connection</span>.<span style="color:#79c0ff">Properties</span>.<span style="color:#79c0ff">PrivateLinkServiceId</span> `</span></span><span style="display:flex;"><span> -To<span style="color:#79c0ff">$TargetObject</span>.<span style="color:#79c0ff">Id</span> `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:privateEndpointGraph</span></span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span>} -End {</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># The completed graphs are validated here.</span></span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p>The sample uses global variables because the state must remain available across
PSRule callback invocations. In a larger rule set, wrap this state in a single object
and reset it before each run.</p><h2 id="validate-complete-dependency-paths" class="ps-heading">Validate complete dependency paths<a class="ps-heading-anchor" href="#validate-complete-dependency-paths" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>Once PSRule reaches the<code>End</code> block, every relevant Bicep resource has been expanded
and processed. We can now ask questions about the deployment as a whole.</p><p>For example, the following check confirms that every Function App has a VNet
integration path:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$webApp</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$global:webSites</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$path</span> = Get-GraphPath `</span></span><span style="display:flex;"><span> -From<span style="color:#79c0ff">$webApp</span> `</span></span><span style="display:flex;"><span> -To<span style="color:#79c0ff">$global:vnetId</span> `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:connectionGraph</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> (<span style="color:#79c0ff">$null</span><span style="color:#ff7b72;font-weight:bold">-eq</span><span style="color:#79c0ff">$path</span>) {</span></span><span style="display:flex;"><span><span style="color:#ff7b72">throw</span><span style="color:#a5d6ff">"No VNet integration path was found for Function App:</span><span style="color:#79c0ff">$webApp</span><span style="color:#a5d6ff">"</span></span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p>We can apply the same idea to private endpoint connectivity:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 8</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679"> 9</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">10</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span><span style="color:#ff7b72">foreach</span> (<span style="color:#79c0ff">$webApp</span><span style="color:#ff7b72">in</span><span style="color:#79c0ff">$global:webSites</span>) {</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$path</span> = Get-GraphPath `</span></span><span style="display:flex;"><span> -From<span style="color:#79c0ff">$webApp</span> `</span></span><span style="display:flex;"><span> -To<span style="color:#79c0ff">$global:vnetId</span> `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:privateEndpointGraph</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> (<span style="color:#79c0ff">$null</span><span style="color:#ff7b72;font-weight:bold">-eq</span><span style="color:#79c0ff">$path</span>) {</span></span><span style="display:flex;"><span><span style="color:#ff7b72">throw</span><span style="color:#a5d6ff">"No private endpoint path was found for Function App:</span><span style="color:#79c0ff">$webApp</span><span style="color:#a5d6ff">"</span></span></span><span style="display:flex;"><span> }</span></span><span style="display:flex;"><span>}</span></span></code></pre></td></tr></table></div></div><p>Throwing from the<code>End</code> block causes the validation run to fail. This works well in
CI, although it does not produce the same detailed assertion output as a normal
PSRule<code>Rule</code> block.</p><p>Run the rules against the Bicep entry point with:</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>Invoke-PSRule -Format File -InputPath ./deployments/non-prod/main.<span style="color:#79c0ff">bicep</span></span></span></code></pre></td></tr></table></div></div><h2 id="export-the-graph-for-troubleshooting" class="ps-heading">Export the graph for troubleshooting<a class="ps-heading-anchor" href="#export-the-graph-for-troubleshooting" aria-label="Link to this section" title="Link to this section"><i class="fas fa-link" aria-hidden="true"/></a></h2><p>The same model used for validation can produce a diagram. This is particularly
helpful when a CI check reports a missing path and you need to see which relationship
was absent. PSQuickGraph can export the graph in Graphviz DOT format; the<a href="https://graphviz.org/download/">Graphviz</a><code>dot</code> executable can then render it as SVG.</p><div class="highlight"><div style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">1</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">2</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">3</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">4</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">5</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">6</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">7</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#737679">8</span></code></pre></td><td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-powershell" data-lang="powershell"><span style="display:flex;"><span>Export-Graph `</span></span><span style="display:flex;"><span> -Graph<span style="color:#79c0ff">$global:privateEndpointGraph</span> `</span></span><span style="display:flex;"><span> -Format Graphviz `</span></span><span style="display:flex;"><span> -Path ./output/private-endpoints.<span style="color:#79c0ff">dot</span></span></span><span style="display:flex;"><span/></span><span style="display:flex;"><span>&amp; dot `</span></span><span style="display:flex;"><span> -Tsvg ./output/private-endpoints.<span style="color:#79c0ff">dot</span> `</span></span><span style="display:flex;"><span> -o ./output/private-endpoints.<span style="color:#79c0ff">svg</span></span></span></code></pre></td></tr></table></div></div><p><img src="/images/articles/psrule-azure-resource-relationships.svg" alt="An Azure Function App connected through a private endpoint and subnet to a virtual network"/><p>This separates the solution into three clear steps:</p><ol><li>PSRule expands Bicep into resource objects.</li><li>A convention converts cross-resource references into graph edges.</li><li>Graph queries validate the architecture after all resources are available.</li></ol><p>The result complements normal per-resource rules instead of replacing them. Use
regular PSRule assertions for local properties and graph assertions for requirements
that span the deployment.</p><p>A complete Bicep project, PSRule configuration, architecture document, and runnable
Codespaces environment are available in the<a href="https://github.com/eosfor/psrule-demo">psrule-demo repository</a>.</p>
]]></content:encoded></item></channel></rss>