<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/feed.xslt"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel>
<title>Web logs of McSinyx</title>
<link>https://lumvok.store</link>
<atom:link href="https://lumvok.store/feed.xml" rel="self" type="application/rss+xml"/>
<description>Random write-ups packed with pop culture references</description>
<copyright><![CDATA[🄯 2019–2024 Nguyễn Gia Phong under CC BY-SA 4.0]]></copyright>
<language>en</language>
<generator>Franklin</generator>
<item>
  <title>System Cascade Connection</title>
  <link>https://lumvok.store/blog/system/index.html</link>
  <guid>https://lumvok.store/blog/system/index.html</guid>
  <description>Properties of cascade connected systems analyzed via anonymous functions</description>
  <category>fun</category><category>math</category>
  <pubDate>Wed, 15 Apr 2020 00:00:00 +0000</pubDate>
  <content:encoded><![CDATA[
<h1 id="system_cascade_connection">System Cascade Connection</h1>
<p>Given two discrete-time systems \(A\) and \(B\) connected in cascade to form a new system \(C = x \mapsto B(A(x))\), we examine the following properties:</p>
<div class="franklin-toc"><ol><li>Linearity</li><li>Time Invariance</li><li>LTI Ordering</li><li>Causality</li><li>BIBO Stability</li></ol></div>
<h2 id="linearity">Linearity</h2>
<p>If \(A\) and \(B\) are linear, i.e. for all signals \(x_i\) and scalars \(a_i\),</p>
\[\begin{aligned}
  A\left(n \mapsto \sum_i a_i x_i[n]\right) = n \mapsto \sum_i a_i A(x_i)[n]\\
  B\left(n \mapsto \sum_i a_i x_i[n]\right) = n \mapsto \sum_i a_i B(x_i)[n]
\end{aligned}\]
<p>then \(C\) is also linear</p>
\[\begin{aligned}
  C\left(n \mapsto \sum_i a_i x_i[n]\right)
  &= B\left(A\left(n \mapsto \sum_i a_i x_i[n]\right)\right)\\
  &= B\left(n \mapsto \sum_i a_i A(x_i)[n]\right)\\
  &= n \mapsto \sum_i a_i B(A(x_i))[n]\\
  &= n \mapsto \sum_i a_i C(x_i)[n]
\end{aligned}\]
<h2 id="time_invariance">Time Invariance</h2>
<p>If \(A\) and \(B\) are time invariant, i.e. for all signals \(x\) and integers \(k\),</p>
\[\begin{aligned}
  A(n \mapsto x[n - k]) &= n \mapsto A(x)[n - k]\\
  B(n \mapsto x[n - k]) &= n \mapsto B(x)[n - k]
\end{aligned}\]
<p>then \(C\) is also time invariant</p>
\[\begin{aligned}
  C(n \mapsto x[n - k])
  &= B(A(n \mapsto x[n - k]))\\
  &= B(n \mapsto A(x)[n - k])\\
  &= n \mapsto B(A(x))[n - k]\\
  &= n \mapsto C(x)[n - k]
\end{aligned}\]
<h2 id="lti_ordering">LTI Ordering</h2>
<p>If \(A\) and \(B\) are linear and time-invariant, there exists signals \(g\) and \(h\) such that for all signals \(x\), \(A = x \mapsto x * g\) and \(B = x \mapsto x * h\), thus </p>
\[B(A(x)) = B(x * g) = x * g * h = x * h * g = A(x * h) = A(B(x))\]
<p>or interchanging \(A\) and \(B\) order does not change \(C\).</p>
<h2 id="causality">Causality</h2>
<p>If \(A\) and \(B\) are causal, i.e. for all signals \(x\), \(y\) and any choise of integer \(k\),</p>
\[\begin{aligned}
  \forall n < k, x[n] = y[n]\quad
  \Longrightarrow &\;\begin{cases}
  \forall n < k, A(x)[n] = A(y)[n]\\
  \forall n < k, B(x)[n] = B(y)[n]
  \end{cases}\\
  \Longrightarrow &\;\forall n < k, B(A(x))[n] = B(A(y))[n]\\
  \Longleftrightarrow &\;\forall n < k, C(x)[n] = C(y)[n]
\end{aligned}\]
<p>then \(C\) is also causal.</p>
<h2 id="bibo_stability">BIBO Stability</h2>
<p>If \(A\) and \(B\) are stable, i.e. there exists a signal \(x\) and scalars \(a\) and \(b\) that for all integers \(n\),</p>
\[\begin{aligned}
  |x[n]| < a &\Longrightarrow |A(x)[n]| < b\\
  |x[n]| < a &\Longrightarrow |B(x)[n]| < b
\end{aligned}\]
<p>then \(C\) is also stable, i.e. there exists a signal \(x\) and scalars \(a\), \(b\) and \(c\) that for all integers \(n\),</p>
\[\begin{aligned}
  |x[n]| < a\quad
  \Longrightarrow &\;|A(x)[n]| < b\\
  \Longrightarrow &\;|B(A(x))[n]| < c\\
  \Longleftrightarrow &\;|C(x)[n]| < c
\end{aligned}\]    <a href="mailto:cnx.site@loa.loang.net?In-Reply-To=%3Cblog/system@cnx%3E&Subject=Re: System Cascade Connection">Reply via email</a>]]></content:encoded>
  <comments><![CDATA[https://lists.sr.ht/~cnx/site?search=In-Reply-To:%3Cblog/system@cnx%3E]]></comments>
  <wfw:commentRss>https://lumvok.store/blog/system/comments.xml</wfw:commentRss>
</item>
<item>
  <title>Infinite Sequences: A Case Study in Functional Python</title>
  <link>https://lumvok.store/blog/conseq/index.html</link>
  <guid>https://lumvok.store/blog/conseq/index.html</guid>
  <description>SICP subsection 3.5.2 in Python</description>
  <category>fun</category><category>math</category><category>python</category>
  <pubDate>Thu, 28 Feb 2019 00:00:00 +0000</pubDate>
  <content:encoded><![CDATA[
<h1>Infinite Sequences: A Case Study in Functional Python</h1>
<p>In this article, we will only consider sequences defined by a function whose domain is a subset of the set of all integers.  Such sequences will be <em>visualized</em>, i.e. we will try to evaluate the first few &#40;thousand&#41; elements, using functional programming paradigm, where functions are more similar to the ones in math &#40;in contrast to imperative style with side effects confusing to inexperenced coders&#41;.  The idea is taken from <a href="https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-24.html#&#37;_sec_3.5.2">subsection 3.5.2 of SICP</a> and adapted to Python, which, compare to Scheme, is significantly more popular: Python is pre-installed on almost every modern Unix-like system, namely macOS, GNU/Linux and the &#42;BSDs; and even at MIT, the new 6.01 in Python has recently replaced the legendary 6.001 &#40;SICP&#41;.</p>
<p>One notable advantage of using Python is its huge <strong>standard</strong> library. For example the <em>identity sequence</em> &#40;sequence defined by the identity function&#41; can be imported directly from <code>itertools</code>:</p>
<pre><code class="language-python">&gt;&gt;&gt; from itertools import count
&gt;&gt;&gt; positive_integers &#61; count&#40;start&#61;1&#41;
&gt;&gt;&gt; next&#40;positive_integers&#41;
1
&gt;&gt;&gt; next&#40;positive_integers&#41;
2
&gt;&gt;&gt; for _ in range&#40;4&#41;: next&#40;positive_integers&#41;
... 
3
4
5
6</code></pre>
<p>To open a Python emulator, simply lauch your terminal and run <code>python</code>. If that is somehow still too struggling, navigate to <a href="https://www.python.org/shell">the interactive shell</a> on Python.org.</p>
<p><em>Let&#39;s get it started</em> with somethings everyone hates: recursively defined sequences, e.g. the famous Fibonacci &#40;\(F_n = F_{n-1} + F_{n-2}\), \(F_1 = 1\) and \(F_0 = 0\)&#41;.  Since <a href="https://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html">Python does not support</a> <a href="https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-11.html#call_footnote_Temp_48">tail recursion</a>, it&#39;s generally <strong>not</strong> a good idea to define anything recursively &#40;which is, ironically, the only trivial <em>functional</em> solution in this case&#41; but since we will only evaluate the first few terms &#40;use the <strong>Tab</strong> key to indent the line when needed&#41;:</p>
<pre><code class="language-python">&gt;&gt;&gt; def fibonacci&#40;n, a&#61;0, b&#61;1&#41;:
...     # To avoid making the code look complicated,
...     # n &lt; 0 is not handled here.
...     return a if n &#61;&#61; 0 else fibonacci&#40;n - 1, b, a &#43; b&#41;
... 
&gt;&gt;&gt; fibo_seq &#61; &#40;fibonacci&#40;n&#41; for n in count&#40;start&#61;0&#41;&#41;
&gt;&gt;&gt; for _ in range&#40;7&#41;: next&#40;fibo_seq&#41;
... 
0
1
1
2
3
5
8</code></pre>
<div class="admonition note"><p class="admonition-title">Note</p><p>The <code>fibo_seq</code> above is just to demonstrate how <code>itertools.count</code> can be use to create an infinite sequence defined by a function. For better performance, the following should be used instead:</p>
<pre><code class="language-python">def fibonacci_sequence&#40;a&#61;0, b&#61;1&#41;:
    yield a
    yield from fibonacci_sequence&#40;b, a&#43;b&#41;</code></pre>
</div>
<p>It is noticable that the elements having been iterated through &#40;using <code>next</code>&#41; will disappear forever in the void &#40;oh no&#33;&#41;, but that is the cost we are willing to pay to save some memory, especially when we need to evaluate a member of &#40;arbitrarily&#41; large index to estimate the sequence&#39;s limit. One case in point is estimating a definite integral using <a href="https://en.wikipedia.org/wiki/Riemann_sum#Left_Riemann_sum">left Riemann sum</a>.</p>
<pre><code class="language-python">def integral&#40;f, a, b&#41;:
    def left_riemann_sum&#40;n&#41;:
        dx &#61; &#40;b-a&#41; / n
        def x&#40;i&#41;: return a &#43; i*dx
        return sum&#40;f&#40;x&#40;i&#41;&#41; for i in range&#40;n&#41;&#41; * dx
    return left_riemann_sum</code></pre>
<p>The function <code>integral&#40;f, a, b&#41;</code> as defined above returns a function taking \(n\) as an argument.  As \(n\to\infty\), its result approaches \(\int_a^b f(x)\mathrm d x\).  For example, we are going to estimate \(\pi\) as the area of a semicircle whose radius is \(\sqrt 2\):</p>
<pre><code class="language-python">&gt;&gt;&gt; from math import sqrt
&gt;&gt;&gt; def semicircle&#40;x&#41;: return sqrt&#40;abs&#40;2 - x*x&#41;&#41;
... 
&gt;&gt;&gt; pi &#61; integral&#40;semicircle, -sqrt&#40;2&#41;, sqrt&#40;2&#41;&#41;
&gt;&gt;&gt; pi_seq &#61; &#40;pi&#40;n&#41; for n in count&#40;start&#61;2&#41;&#41;
&gt;&gt;&gt; for _ in range&#40;3&#41;: next&#40;pi_seq&#41;
... 
2.000000029802323
2.514157464087051
2.7320508224700384</code></pre>
<p>Whilst the first few aren&#39;t quite close, at index around 1000, the result is somewhat acceptable:</p>
<pre><code class="language-julia">3.1414873191059525
3.1414874770617427
3.1414876346231577</code></pre>
<p>Since we are comfortable with sequence of sums, let&#39;s move on to sums of a sequence, which are called series.  For estimation, again, we are going to make use of infinite sequences of partial sums, which are implemented as <code>itertools.accumulate</code> by thoughtful Python developers.  <a href="https://en.wikipedia.org/wiki/Geometric_series">Geometric</a> and <a href="https://math.oregonstate.edu/home/programs/undergrad/CalculusQuestStudyGuides/SandS/SeriesTests/p-series.html">p-series</a> can be defined as follow:</p>
<pre><code class="language-python">from itertools import accumulate as partial_sumsdef geometric_series&#40;r, a&#61;1&#41;:
    return partial_sums&#40;a*r**n for n in count&#40;0&#41;&#41;def p_series&#40;p&#41;:
    return partial_sums&#40;1 / n**p for n in count&#40;1&#41;&#41;</code></pre>
<p>We can then use these to determine whether a series is convergent or divergent. For instance, one can easily verify that the \(p\)-series with \(p = 2\) converges to \(\pi^2 / 6 \approx 1.6449340668482264\) via</p>
<pre><code class="language-python">&gt;&gt;&gt; s &#61; p_series&#40;p&#61;2&#41;
&gt;&gt;&gt; for _ in range&#40;11&#41;: next&#40;s&#41;
... 
1.0
1.25
1.3611111111111112
1.4236111111111112
1.4636111111111112
1.4913888888888889
1.511797052154195
1.527422052154195
1.5397677311665408
1.5497677311665408
1.558032193976458</code></pre>
<p>We can observe that it takes quite a lot of steps to get the precision we would generally expect &#40;\(s_{11}\) is only precise to the first decimal place; second decimal places: \(s_{101}\); third: \(s_{2304}\)&#41;. Luckily, many techniques for series acceleration are available. <a href="https://en.wikipedia.org/wiki/Shanks_transformation">Shanks transformation</a> for instance, can be implemented as follow:</p>
<pre><code class="language-python">from itertools import islice, teedef shanks&#40;seq&#41;:
    return map&#40;lambda x, y, z: &#40;x*z - y*y&#41; / &#40;x &#43; z - y*2&#41;,
               *&#40;islice&#40;t, i, None&#41; for i, t in enumerate&#40;tee&#40;seq, 3&#41;&#41;&#41;&#41;</code></pre>
<p>In the code above, <code>lambda x, y, z: &#40;x*z - y*y&#41; / &#40;x &#43; z - y*2&#41;</code> denotes the anonymous function \((x, y, z) \mapsto \frac{xz - y^2}{x + z - 2y}\) and <code>map</code> is a higher order function applying that function to respective elements of subsequences starting from index 1, 2 and 3 of <code>seq</code>. On Python 2, one should import <code>imap</code> from <code>itertools</code> to get the same <a href="https://en.wikipedia.org/wiki/Lazy_evaluation">lazy</a> behavior of <code>map</code> on Python 3.</p>
<pre><code class="language-python">&gt;&gt;&gt; s &#61; shanks&#40;p_series&#40;2&#41;&#41;
&gt;&gt;&gt; for _ in range&#40;10&#41;: next&#40;s&#41;
... 
1.4500000000000002
1.503968253968257
1.53472222222223
1.5545202020202133
1.5683119658120213
1.57846371882088
1.5862455815659202
1.5923993101138652
1.5973867787856946
1.6015104548459742</code></pre>
<p>The result was quite satisfying, yet we can do one step futher by continuously applying the transformation to the sequence:</p>
<pre><code class="language-python">&gt;&gt;&gt; def compose&#40;transform, seq&#41;:
... 	yield next&#40;seq&#41;
... 	yield from compose&#40;transform, transform&#40;seq&#41;&#41;
... 
&gt;&gt;&gt; s &#61; compose&#40;shanks, p_series&#40;2&#41;&#41;
&gt;&gt;&gt; for _ in range&#40;10&#41;: next&#40;s&#41;
... 
1.0
1.503968253968257
1.5999812811165188
1.6284732442271674
1.6384666832276524
1.642311342667821
1.6425249569252578
1.640277484549416
1.6415443295058203
1.642038043478661</code></pre>
<p>Shanks transformation works on every sequence &#40;not just sequences of partial sums&#41;.  Back to previous example of using left Riemann sum to compute definite integral:</p>
<pre><code class="language-python">&gt;&gt;&gt; pi_seq &#61; compose&#40;shanks, map&#40;pi, count&#40;2&#41;&#41;&#41;
&gt;&gt;&gt; for _ in range&#40;10&#41;: next&#40;pi_seq&#41;
... 
2.000000029802323
2.978391111182236
3.105916845397819
3.1323116570377185
3.1389379264270736
3.140788413965646
3.140921512857936
3.1400282163913436
3.1400874774021816
3.1407097229603256
&gt;&gt;&gt; next&#40;islice&#40;pi_seq, 300, None&#41;&#41;
3.1415061302492413</code></pre>
<p>Now having series defined, let&#39;s see if we can learn anything about power series. Sequence of partial sums of power series \(\sum c_n (x - a)^n\) can be defined as</p>
<pre><code class="language-python">from operator import muldef power_series&#40;c, start&#61;0, a&#61;0&#41;:
    return lambda x: partial_sums&#40;map&#40;mul, c, &#40;x**n for n in count&#40;start&#41;&#41;&#41;&#41;</code></pre>
<p>We can use this to compute functions that can be written as <a href="https://en.wikipedia.org/wiki/Taylor_series">Taylor series</a>:</p>
<pre><code class="language-python">from math import factorial
def exp&#40;x&#41;:
    return power_series&#40;1/factorial&#40;n&#41; for n in count&#40;0&#41;&#41;&#40;x&#41;def cos&#40;x&#41;:
    c &#61; &#40;&#40;1 - n&#37;2&#41; * &#40;1 - n&#37;4&#41; / factorial&#40;n&#41; for n in count&#40;0&#41;&#41;
    return power_series&#40;c&#41;&#40;x&#41;def sin&#40;x&#41;:
    c &#61; &#40;n&#37;2 * &#40;2 - n&#37;4&#41; / factorial&#40;n&#41; for n in count&#40;1&#41;&#41;
    return power_series&#40;c, start&#61;1&#41;&#40;x&#41;</code></pre>
<p>Amazing&#33;  Let&#39;s test &#39;em&#33;</p>
<pre><code class="language-python">&gt;&gt;&gt; e &#61; compose&#40;shanks, exp&#40;1&#41;&#41; # this should converges to 2.718281828459045
&gt;&gt;&gt; for _ in range&#40;4&#41;: next&#40;e&#41;
... 
1.0
2.749999999999996
2.718276515152136
2.718281825486623</code></pre>
<p>Impressive, huh? For sine and cosine, series acceleration is not even necessary:</p>
<pre><code class="language-python">&gt;&gt;&gt; from math import pi as PI
&gt;&gt;&gt; s &#61; sin&#40;PI/6&#41;
&gt;&gt;&gt; for _ in range&#40;5&#41;: next&#40;s&#41;
... 
0.5235987755982988
0.5235987755982988
0.49967417939436376
0.49967417939436376
0.5000021325887924
&gt;&gt;&gt; next&#40;islice&#40;cos&#40;PI/3&#41;, 8, None&#41;&#41;
0.500000433432915</code></pre>
<p></p>    <a href="mailto:cnx.site@loa.loang.net?In-Reply-To=%3Cblog/conseq@cnx%3E&Subject=Re: Infinite Sequences: A Case Study in Functional Python">Reply via email</a>]]></content:encoded>
  <comments><![CDATA[https://lists.sr.ht/~cnx/site?search=In-Reply-To:%3Cblog/conseq@cnx%3E]]></comments>
  <wfw:commentRss>https://lumvok.store/blog/conseq/comments.xml</wfw:commentRss>
</item>
</channel></rss>