<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>pedagogical | Esra'a Saleh</title><link>https://esraasaleh.com/tag/pedagogical/</link><atom:link href="https://esraasaleh.com/tag/pedagogical/index.xml" rel="self" type="application/rss+xml"/><description>pedagogical</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Fri, 11 Sep 2026 00:00:00 +0000</lastBuildDate><image><url>https://esraasaleh.com/media/icon_hu0b7a4cb9992c9ac0e91bd28ffd38dd00_9727_512x512_fill_lanczos_center_3.png</url><title>pedagogical</title><link>https://esraasaleh.com/tag/pedagogical/</link></image><item><title>Intuitive RL Series : Contraction Operators</title><link>https://esraasaleh.com/cs-blog/intuitive-rl-contraction-operators/</link><pubDate>Fri, 11 Sep 2026 00:00:00 +0000</pubDate><guid>https://esraasaleh.com/cs-blog/intuitive-rl-contraction-operators/</guid><description>&lt;p>If you are teaching or learning RL and have ever wished an idea showed up as a picture first, this series is for you. I am trying to communicate science the way I always wanted it communicated to me: precision and clarity, with as few words as I can get away with (written or spoken), as much visual structure as I can find, and a clear sense of where each stepping stone sits in the larger picture. I recognize this is not a universal preference, and I am targeting a specific kind of reader. This first post is intentionally simple so I can learn, from your feedback, what actually works for people who share my communication preferences. Main ideas from &lt;em>Reinforcement Learning: Foundations&lt;/em> &lt;a href="#ref1">[1]&lt;/a>. Videos use Manim &lt;a href="#ref2">[2]&lt;/a>, 3Blue1Brown&amp;rsquo;s animation tool.&lt;/p>
&lt;hr>
&lt;p>Let&amp;rsquo;s start!&lt;/p>
&lt;p>Click 💡 whenever you want more intuition.&lt;/p>
&lt;p>&lt;strong>Contraction operators&lt;/strong> keep showing up in RL proofs. They are a main tool for reasoning about convergence: if we keep applying the same update, do we settle on a solution?&lt;/p>
&lt;p>Contractions are defined using norms.&lt;/p>
&lt;p>A &lt;strong>norm&lt;/strong> is a function
$\|\cdot\| : \mathbb{R}^d \to [0, \infty)$ such that
$\forall\, x, y \in \mathbb{R}^d,\; \forall\, a \in \mathbb{R}$&lt;/p>
&lt;style>
.norm-list { margin: 0.4rem 0 1rem; padding-left: 1.4rem; }
.norm-list > li { margin: 0.25rem 0 0.7rem; }
.norm-list .norm-row { display: flex; flex-wrap: wrap; align-items: center; column-gap: 0.45rem; }
.norm-list .norm-row > p { display: inline; margin: 0; }
.norm-list .norm-toggle { display: contents; }
.norm-list .norm-toggle summary { cursor: pointer; user-select: none; font-size: 1.25rem; display: inline-block; line-height: 1; }
.norm-list .norm-toggle summary::-webkit-details-marker { display: none; }
.norm-list .norm-intuition { flex: 1 1 100%; width: 100%; margin-top: 0.35rem; }
.norm-list .norm-toggle p { margin: 0.2rem 0 0.35rem; font-size: 0.95rem; }
&lt;/style>
&lt;ol class="norm-list">
&lt;li>
&lt;div class="norm-row">
$\|ax\| = |a| \cdot \|x\|$
&lt;details class="norm-toggle">
&lt;summary aria-label="Idea">💡&lt;/summary>
&lt;div class="norm-intuition">
&lt;p>Scaling a vector by
$a$ scales its size by
$|a|$.&lt;/p>
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 560 180" width="560" height="180" style="max-width:100%;height:auto" role="img" aria-label="OK: -2x is twice as long as x and flipped. Not OK: a claimed -2x with the same length as x.">
&lt;defs>
&lt;marker id="n1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
&lt;path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor"/>
&lt;/marker>
&lt;/defs>
&lt;g fill="currentColor" font-family="ui-sans-serif, system-ui, sans-serif">
&lt;text x="28" y="24" font-size="15" font-weight="700">OK&lt;/text>
&lt;text x="308" y="24" font-size="15" font-weight="700">not OK&lt;/text>
&lt;/g>
&lt;line x1="280" y1="8" x2="280" y2="172" stroke="currentColor" stroke-width="1" opacity="0.35"/>
&lt;g fill="none" stroke="currentColor" stroke-width="2">
&lt;line x1="40" y1="70" x2="150" y2="70" marker-end="url(#n1)"/>
&lt;line x1="260" y1="125" x2="40" y2="125" marker-end="url(#n1)"/>
&lt;line x1="320" y1="70" x2="430" y2="70" marker-end="url(#n1)"/>
&lt;line x1="430" y1="125" x2="320" y2="125" marker-end="url(#n1)"/>
&lt;/g>
&lt;g fill="currentColor" font-size="14" font-family="ui-sans-serif, system-ui, sans-serif" text-anchor="middle">
&lt;text x="95" y="94">x&lt;/text>
&lt;text x="150" y="149">−2x&lt;/text>
&lt;text x="375" y="94">x&lt;/text>
&lt;text x="375" y="149">−2x&lt;/text>
&lt;/g>
&lt;/svg>
&lt;/div>
&lt;/details>
&lt;/div>
&lt;/li>
&lt;li>
&lt;div class="norm-row">
$\|x + y\| \le \|x\| + \|y\|$
&lt;details class="norm-toggle">
&lt;summary aria-label="Idea">💡&lt;/summary>
&lt;div class="norm-intuition">
&lt;p>The size of a sum is at most the sum of the sizes.&lt;/p>
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 560 170" width="560" height="170" style="max-width:100%;height:auto" role="img" aria-label="OK when x+y is shorter than x then y; not OK when it is longer.">
&lt;defs>
&lt;marker id="n2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
&lt;path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor"/>
&lt;/marker>
&lt;/defs>
&lt;g fill="currentColor" font-family="ui-sans-serif, system-ui, sans-serif">
&lt;text x="28" y="22" font-size="15" font-weight="700">OK&lt;/text>
&lt;text x="308" y="22" font-size="15" font-weight="700">not OK&lt;/text>
&lt;/g>
&lt;line x1="280" y1="8" x2="280" y2="162" stroke="currentColor" stroke-width="1" opacity="0.35"/>
&lt;g fill="none" stroke="currentColor" stroke-width="2">
&lt;line x1="40" y1="60" x2="140" y2="60" marker-end="url(#n2)"/>
&lt;line x1="140" y1="60" x2="240" y2="60" marker-end="url(#n2)"/>
&lt;line x1="40" y1="115" x2="206" y2="115" marker-end="url(#n2)"/>
&lt;line x1="320" y1="60" x2="401" y2="60" marker-end="url(#n2)"/>
&lt;line x1="401" y1="60" x2="445" y2="60" marker-end="url(#n2)"/>
&lt;line x1="320" y1="115" x2="520" y2="115" marker-end="url(#n2)"/>
&lt;/g>
&lt;g stroke="currentColor" stroke-width="1" opacity="0.35">
&lt;line x1="240" y1="48" x2="240" y2="128"/>
&lt;line x1="445" y1="48" x2="445" y2="128"/>
&lt;/g>
&lt;g fill="currentColor" font-size="14" font-family="ui-sans-serif, system-ui, sans-serif" text-anchor="middle">
&lt;text x="90" y="84">x&lt;/text>
&lt;text x="190" y="84">y&lt;/text>
&lt;text x="123" y="139">x+y&lt;/text>
&lt;text x="360" y="84">x&lt;/text>
&lt;text x="423" y="84">y&lt;/text>
&lt;text x="475" y="139">x+y&lt;/text>
&lt;/g>
&lt;/svg>
&lt;/div>
&lt;/details>
&lt;/div>
&lt;/li>
&lt;li>
&lt;div class="norm-row">
$\|x\| = 0 \implies x = 0$
&lt;details class="norm-toggle">
&lt;summary aria-label="Idea">💡&lt;/summary>
&lt;div class="norm-intuition">
&lt;p>Size zero means the zero vector.&lt;/p>
&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 560 210" width="560" height="210" style="max-width:100%;height:auto" role="img" aria-label="OK: the zero vector has size zero. Not OK: a nonzero vector claimed to have size zero.">
&lt;defs>
&lt;marker id="n3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
&lt;path d="M 0 0 L 10 5 L 0 10 z" fill="currentColor"/>
&lt;/marker>
&lt;/defs>
&lt;g fill="currentColor" font-family="ui-sans-serif, system-ui, sans-serif">
&lt;text x="28" y="24" font-size="15" font-weight="700">OK&lt;/text>
&lt;text x="308" y="24" font-size="15" font-weight="700">not OK&lt;/text>
&lt;/g>
&lt;line x1="280" y1="8" x2="280" y2="202" stroke="currentColor" stroke-width="1" opacity="0.35"/>
&lt;circle cx="140" cy="110" r="5" fill="currentColor"/>
&lt;circle cx="340" cy="110" r="5" fill="currentColor"/>
&lt;g fill="none" stroke="currentColor" stroke-width="2">
&lt;line x1="340" y1="110" x2="500" y2="55" marker-end="url(#n3)"/>
&lt;/g>
&lt;g fill="currentColor" font-size="14" font-family="ui-sans-serif, system-ui, sans-serif" text-anchor="middle">
&lt;text x="140" y="52">‖0‖ = 0&lt;/text>
&lt;text x="140" y="138">0&lt;/text>
&lt;text x="340" y="138">0&lt;/text>
&lt;text x="420" y="175">‖x‖ = 0&lt;/text>
&lt;text x="518" y="48">x ≠ 0&lt;/text>
&lt;/g>
&lt;/svg>
&lt;/div>
&lt;/details>
&lt;/div>
&lt;/li>
&lt;/ol>
&lt;p>Examples:&lt;/p>
&lt;ul class="norm-examples">
&lt;li>
the
$p$-norm
$\|x\|_p = \big(\sum_i |x_i|^p\big)^{1/p}$ for
$p \ge 1$
&lt;ul>
&lt;li>Euclidean:
$p = 2$&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
the max-norm
$\|x\|_\infty = \max_i |x_i|$
&lt;/li>
&lt;/ul>
&lt;style>
.norm-examples > li { margin: 0 0 1.15rem; }
.norm-examples > li:last-child { margin-bottom: 0; }
.norm-examples ul { margin: 0.4rem 0 0; }
&lt;/style>
&lt;p>An &lt;strong>operator&lt;/strong> is a function between spaces. Here, real vector spaces.&lt;/p>
$$
T : \mathbb{R}^d \to \mathbb{R}^d,
\qquad
T(v) \in \mathbb{R}^d
$$
&lt;p>Applied
$n$ times:&lt;/p>
$$
T^n(v) = T\big(T^{n-1}(v)\big), \qquad n \ge 2,
$$
&lt;p>
$T$ is a &lt;strong>contraction operator&lt;/strong> (or specifically a &lt;strong>
$\boldsymbol{\beta}$-contraction operator&lt;/strong>) w.r.t.
$\|\cdot\|$ if&lt;/p>
$$
\forall\, v_1, v_2 \in \mathbb{R}^d \qquad \|T(v_1) - T(v_2)\| \le \beta \, \|v_1 - v_2\|.
$$
$$
\text{where } \beta \text{ is } \in (0, 1)
$$
&lt;h2 id="references">References&lt;/h2>
&lt;p>&lt;a id="ref1">&lt;/a>[1] S. Mannor, Y. Mansour, and A. Tamar, &lt;em>Reinforcement Learning: Foundations&lt;/em>. Cambridge, U.K.: Cambridge University Press, 2026. [Online]. Available: &lt;a href="https://sites.google.com/view/rlfoundations/home" target="_blank" rel="noopener">https://sites.google.com/view/rlfoundations/home&lt;/a>&lt;/p>
&lt;p>&lt;a id="ref2">&lt;/a>[2] G. Sanderson, &lt;em>Manim&lt;/em>, 3Blue1Brown. [Online]. Available: &lt;a href="https://github.com/3b1b/manim" target="_blank" rel="noopener">https://github.com/3b1b/manim&lt;/a>&lt;/p></description></item></channel></rss>