Jekyll2023-10-05T18:48:05+00:00https://gramian.github.io/numerical-schemer.xyz/feed.xmlThe Numerical SchemerNumerics with Scheme
Christian HimpeMatrico Algorithms III2023-10-05T00:00:00+00:002023-10-05T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2023/10/05/matrico-algorithms-iii<h2 id="matricos-differential-equation-solvers"><code class="language-plaintext highlighter-rouge">matrico</code>’s Differential Equation Solvers</h2>
<ul>
<li><a href="http://numerical-schemer.xyz/2023/07/10/matrico-algorithms-i.html">Part I</a></li>
<li><a href="http://numerical-schemer.xyz/2023/08/21/matrico-algorithms-ii.html">Part II</a></li>
<li>Part III</li>
</ul>
<h3 id="the-significance-differential-equations">The Significance Differential Equations</h3>
<p>Differential equations are mathematical problems described by differentials of the solution function <code class="language-plaintext highlighter-rouge">x</code>.
Ordinary differential equations (ODEs) are the basic type of differential equations in one variable,
for example, time <code class="language-plaintext highlighter-rouge">t</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ẋ(t) = f(t,x(t))
</code></pre></div></div>
<p>Dynamical systems, systems evolving over time defined by their derivatives, can be described by such ODEs.
Note, that this is already a special case of an ODE, particularly,
the single first-order differential is already isolated (explicit).
More generally, an ODE can be of the implicit form:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 = f(t,x(t),ẋ(t),ẍ(t),…)
</code></pre></div></div>
<p>Partial differential equations (PDEs) are an even more general type of differential equations in multiple variables,
for example one spatial and one time dimension.
Numerically, PDEs are typically first reduced to ODEs (by means of discretization), which are then solved in the remaining dimension again by discretization.
This makes solvers for ODEs so important.</p>
<p>Generally, most numerical solvers work by <a href="https://en.wikipedia.org/wiki/Discretization">discretization</a>,
which means the solution function’s continuous argument variable is replaced by a set of samples,
for example a regular grid.
A famous and popular class of numerical ODE solvers are <a href="https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods#Explicit_Runge%E2%80%93Kutta_methods">Runge-Kutta (RK) methods</a>.
RK methods can be categorized as implicit and explicit.
The former (implicit) methods allow the solution of <a href="https://en.wikipedia.org/wiki/Stiff_equation">stiff systems</a> but require the solution of a linear system (for linear implicit systems),
or a root-finding problem (for nonlinear implicit systems), in each step;
the latter (explicit) systems compute the next discretization step only based on previous steps.
<code class="language-plaintext highlighter-rouge">matrico</code> provides only explicit RK methods, but given the QR decomposition, simple yet powerful methods
like the first-order diagonally implicit RK (DIRK) method can be implemented with <code class="language-plaintext highlighter-rouge">matrico</code>.</p>
<p>Before, looking at the solver algorithms, I introduce an extension to ODEs that is central to
<a href="https://en.wikipedia.org/wiki/Control_theory">system theory and control theory</a>: <a href="https://en.wikipedia.org/wiki/State-space_representation">Input-output systems</a>.
An input-output system consists of an ODE (in <code class="language-plaintext highlighter-rouge">x</code>), and an output function <code class="language-plaintext highlighter-rouge">y</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ẋ(t) = f(t,x(t))
y(t) = g(t,x(t))
</code></pre></div></div>
<p>Note that this is a one-directional coupling, meaning <code class="language-plaintext highlighter-rouge">f</code> cannot depend on <code class="language-plaintext highlighter-rouge">y</code>.
Again, a more general case would be a differential algebraic equation system,
coupling bi-directionally differential equations and algebraic equations.
However, <code class="language-plaintext highlighter-rouge">matrico</code> supports solving not only ODEs but input-output system directly.</p>
<h3 id="matricos-generic-input-output-system-time-stepper"><code class="language-plaintext highlighter-rouge">matrico</code>’s Generic Input-Output System Time Stepper</h3>
<p>The explicit RK single-step solvers provided by <code class="language-plaintext highlighter-rouge">matrico</code> are implemented in two parts:
A generic <code class="language-plaintext highlighter-rouge">stepper</code> function and another function defining the specifics of the solver algorithm.
The <code class="language-plaintext highlighter-rouge">stepper</code> handles the discretization and wraps the solver
and returns the solution trajectory as matrix,
where each column represents a discretization step’s solution.
Practically, this is implemented as a tail recursion:</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">stepper</span> <span class="nv">typ</span> <span class="nv">sys</span> <span class="nv">tim</span> <span class="nv">x0</span><span class="p">)</span>
<span class="p">(</span><span class="k">let</span> <span class="p">[(</span><span class="nf">vec</span> <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">pair?</span> <span class="nv">sys</span><span class="p">)</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">sys</span><span class="p">)</span> <span class="nv">sys</span><span class="p">))</span>
<span class="p">(</span><span class="nf">out</span> <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">pair?</span> <span class="nv">sys</span><span class="p">)</span> <span class="p">(</span><span class="nf">tail</span> <span class="nv">sys</span><span class="p">)</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">x</span> <span class="nv">y</span><span class="p">)</span> <span class="nv">y</span><span class="p">)))</span>
<span class="p">(</span><span class="nf">dt</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">tim</span><span class="p">))</span>
<span class="p">(</span><span class="nf">tf</span> <span class="p">(</span><span class="nf">tail</span> <span class="nv">tim</span><span class="p">))]</span>
<span class="p">(</span><span class="k">let</span> <span class="nv">rho</span> <span class="p">[(</span><span class="nf">step</span> <span class="mf">0.0</span><span class="p">)</span>
<span class="p">(</span><span class="nf">state</span> <span class="nv">x0</span><span class="p">)</span>
<span class="p">(</span><span class="nf">series</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">out</span> <span class="mf">0.0</span> <span class="nv">x0</span><span class="p">)))]</span>
<span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">fp></span> <span class="nv">step</span> <span class="nv">tf</span><span class="p">)</span> <span class="p">(</span><span class="nf">matrix-implode</span> <span class="p">(</span><span class="nb">reverse</span> <span class="nv">series</span><span class="p">))</span>
<span class="p">(</span><span class="k">let</span> <span class="p">[(</span><span class="nf">next</span> <span class="p">(</span><span class="nf">typ</span> <span class="nv">vec</span> <span class="nv">dt</span> <span class="nv">step</span> <span class="nv">state</span><span class="p">))]</span>
<span class="p">(</span><span class="nf">rho</span> <span class="p">(</span><span class="nf">fp+</span> <span class="nv">step</span> <span class="nv">dt</span><span class="p">)</span> <span class="nv">next</span> <span class="p">(</span><span class="nb">cons</span> <span class="p">(</span><span class="nf">out</span> <span class="nv">step</span> <span class="nv">next</span><span class="p">)</span> <span class="nv">series</span><span class="p">)))))))</span>
</code></pre></div></div>
<p>Depending on whether a vector field or a pair of a vector field and an output function is given,
either an ODE system or an input-output system is approximately solved.</p>
<h3 id="explicit-runge-kutta-steppers">Explicit Runge-Kutta Steppers</h3>
<p><code class="language-plaintext highlighter-rouge">matrico</code> provides two families of explicit Runge Kutta methods,
where “explicit” refers to the direct evaluability of the next step, given the previous step.
There is a multitude of RK methods which emphasize certain solution properties.
The core property is convergence of a certain order,
where the order determines what order of polynomials can be approximated numerically exact.
However, in case of RK methods, there are degrees of freedom beyond those required for convergence.
These extra degrees of freedom can be used to optimize for other properties.
Typically, stability is a property that is optimized for.
Among the many stability concepts of RK methods, the <code class="language-plaintext highlighter-rouge">matrico</code> solvers aim to maximize
the <a href="https://en.wikipedia.org/wiki/Stiff_equation#A-stability">region of absolute stability</a>
in certain ways.
This stability needs to be scalable, meaning the computational complexity and the stability region expanse
can be balanced; so, the more computational effort is invested, the larger the stability region.</p>
<p>Furthermore, both RK methods provided by <code class="language-plaintext highlighter-rouge">matrico</code> also feature minimal memory requirements.
This means in terms of RK methods two registers (vectors) of the problem dimension,
one as the global accumulator (accumulating over all steps),
and one as local accumulator (accumulating over a step’s sub-steps).
Details about memory efficiency of RK methods can be found in “<a href="https://doi.org/10.1016/j.jcp.2009.11.006">Runge–Kutta methods with minimum storage implementations</a>”.</p>
<h3 id="the-2nd-order-ssp-runge-kutta-method">The 2nd Order SSP Runge-Kutta Method</h3>
<p>The first family of RK methods <code class="language-plaintext highlighter-rouge">matrico</code> provides is a second-order accurate method,
which in its 2-stage form is equivalent to the <a href="https://en.wikipedia.org/wiki/Heun%27s_method">Heun method</a>.
This method is 2nd order optimal strong stability preserving (SSP),
meaning their <a href="https://en.wikipedia.org/wiki/Total_variation_diminishing">total variation</a> does not increase over time.
While total variation is a useful property (particularly for hyperbolic PDEs),
more important is the per-stage increase of stability in all directions but positive real (right).</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">mx-ode2-ssp</span> <span class="nv">num</span> <span class="nv">sys</span> <span class="nv">tim</span> <span class="nv">x0</span><span class="p">)</span>
<span class="p">(</span><span class="k">let*</span> <span class="p">[(</span><span class="nf">s-1</span> <span class="p">(</span><span class="nf">fp</span> <span class="p">(</span><span class="nf">fx-1</span> <span class="nv">num</span><span class="p">)))</span>
<span class="p">(</span><span class="nf">ssp</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">vf</span> <span class="nv">dt</span> <span class="nv">tk</span> <span class="nv">xk</span><span class="p">)</span>
<span class="p">(</span><span class="k">let</span> <span class="p">[(</span><span class="nf">dt/s-1</span> <span class="p">(</span><span class="nf">fp/</span> <span class="nv">dt</span> <span class="nv">s-1</span><span class="p">))]</span>
<span class="p">(</span><span class="k">let</span> <span class="nv">rho</span> <span class="p">[(</span><span class="nf">cur</span> <span class="p">(</span><span class="nf">fx-1</span> <span class="nv">num</span><span class="p">))</span>
<span class="p">(</span><span class="nf">c</span> <span class="nv">tk</span><span class="p">)</span>
<span class="p">(</span><span class="nf">ret</span> <span class="nv">xk</span><span class="p">)]</span>
<span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">fx=0?</span> <span class="nv">cur</span><span class="p">)</span> <span class="p">(</span><span class="nf">mx/</span> <span class="p">(</span><span class="nf">mx-axpy</span> <span class="nv">dt</span> <span class="p">(</span><span class="nf">vf</span> <span class="nv">c</span> <span class="nv">ret</span><span class="p">)</span> <span class="p">(</span><span class="nf">mx-axpy</span> <span class="nv">s-1</span> <span class="nv">ret</span> <span class="nv">xk</span><span class="p">))</span> <span class="p">(</span><span class="nf">fp</span> <span class="nv">num</span><span class="p">))</span>
<span class="p">(</span><span class="nf">rho</span> <span class="p">(</span><span class="nf">fx-1</span> <span class="nv">cur</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp+</span> <span class="nv">tk</span> <span class="nv">dt/s-1</span><span class="p">)</span> <span class="p">(</span><span class="nf">mx-axpy</span> <span class="nv">dt/s-1</span> <span class="p">(</span><span class="nf">vf</span> <span class="nv">c</span> <span class="nv">ret</span><span class="p">)</span> <span class="nv">ret</span><span class="p">)))))))]</span>
<span class="p">(</span><span class="nf">time-stepper</span> <span class="nv">ssp</span> <span class="nv">sys</span> <span class="nv">tim</span> <span class="nv">x0</span><span class="p">)))</span>
</code></pre></div></div>
<p>For more information about this RK solver, see the paper:
“<a href="https://doi.org/10.1137/07070485X">Highly Efficient Strong Stability-Preserving Runge-Kutta Methods with Low-Storage Implementations</a>”.</p>
<h3 id="the-2nd-order-hyperbolic-runge-kutta-methods">The 2nd Order Hyperbolic Runge-Kutta Methods</h3>
<p>This is a second-order accurate method, which in its 2-stage form is equivalent to the <a href="https://en.wikipedia.org/wiki/Midpoint_method">explicit midpoint method</a>.
The extra degrees of freedom are used for this method to maximize the region of stability in the positive imaginary direction, and is in fact optimal in this regard.
This has the benefit of increased stability for systems that result from spatial discretization of
hyperbolic PDEs, thus the name hyperbolic RK method.
The table of values used for the algorithm’s coefficients are taken from “<a href="https://doi.org/10.1002/pamm.202200201">System Order Reduction for Gas and Energy Networks</a>”.</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">mx-ode2-hyp</span> <span class="nv">num</span> <span class="nv">sys</span> <span class="nv">tim</span> <span class="nv">x0</span><span class="p">)</span>
<span class="p">(</span><span class="k">let*</span> <span class="p">[(</span><span class="nf">coeff</span> <span class="p">(</span><span class="k">case</span> <span class="nv">num</span> <span class="p">[</span> <span class="p">(</span><span class="nf">2</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span> <span class="p">(</span><span class="nf">3</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span> <span class="p">(</span><span class="nf">4</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">4</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">6</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span> <span class="p">(</span><span class="nf">5</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">5</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">10</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span> <span class="p">(</span><span class="nf">6</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">6</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">15</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">4</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">24</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span> <span class="p">(</span><span class="nf">7</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">7</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">21</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">5</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">35</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span> <span class="p">(</span><span class="nf">8</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">28</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">6</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">48</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">4</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span> <span class="p">(</span><span class="nf">9</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">9</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">36</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">7</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">63</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">5</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">5</span> <span class="mi">21</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[(</span><span class="nf">10</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">10</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">45</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">80</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">6</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">9</span> <span class="mi">50</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">4</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[(</span><span class="nf">11</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">11</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">55</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">9</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">99</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">7</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">14</span> <span class="mi">99</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">5</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">33</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[(</span><span class="nf">12</span><span class="p">)</span> <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">12</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">2</span> <span class="mi">66</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">10</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">8</span> <span class="mi">120</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">4</span> <span class="mi">35</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">6</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">14</span> <span class="mi">75</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">4</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">3</span><span class="p">)</span> <span class="p">(</span><span class="nf">fp%</span> <span class="mi">1</span> <span class="mi">2</span><span class="p">)</span> <span class="mf">1.0</span><span class="p">)]</span>
<span class="p">[</span><span class="k">else</span> <span class="p">(</span><span class="nf">error</span> <span class="ss">'mx-ode2-hyp</span> <span class="s">"Invalid number of stages, should be in [2,12]."</span><span class="p">)]))</span>
<span class="p">(</span><span class="nf">hyp</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">vf</span> <span class="nv">dt</span> <span class="nv">tk</span> <span class="nv">xk</span><span class="p">)</span>
<span class="p">(</span><span class="k">let</span> <span class="nv">rho</span> <span class="p">[(</span><span class="nf">cur</span> <span class="nv">coeff</span><span class="p">)</span>
<span class="p">(</span><span class="nf">ret</span> <span class="nv">xk</span><span class="p">)]</span>
<span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">empty?</span> <span class="nv">cur</span><span class="p">)</span> <span class="nv">ret</span>
<span class="p">(</span><span class="nf">rho</span> <span class="p">(</span><span class="nf">tail</span> <span class="nv">cur</span><span class="p">)</span> <span class="p">(</span><span class="nf">mx-axpy</span> <span class="p">(</span><span class="nf">fp*</span> <span class="nv">dt</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">cur</span><span class="p">))</span> <span class="p">(</span><span class="nf">vf</span> <span class="p">(</span><span class="nf">fp*+</span> <span class="nv">dt</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">cur</span><span class="p">)</span> <span class="nv">tk</span><span class="p">)</span> <span class="nv">ret</span><span class="p">)</span> <span class="nv">xk</span><span class="p">))))))]</span>
<span class="p">(</span><span class="nf">time-stepper</span> <span class="nv">hyp</span> <span class="nv">sys</span> <span class="nv">tim</span> <span class="nv">x0</span><span class="p">)))</span>
</code></pre></div></div>
<p>For more information about this method, see the paper:
“<a href="https://doi.org/10.1016/0378-4754(84)90056-9">One Step Integration Methods of Third-Fourth Order Accuracy with Large Hyperbolic Stability Limits</a>”.</p>
<p>Now interestingly, it looks like there is a pattern to these values,
but I was not yet able to figure it out.
If one would, coefficients for an arbitrary number of stages could be generated procedurally.</p>
<p>Lastly, I want to mention that some of the papers may be behind a paywall,
but a <a href="https://scholar.google.com">Google Scholar</a> search for the titles may yield a publicly accessible version of these works.
And, if I find time, I will add the plots for the stability regions of these RK methods.</p>
<p>This concludes the series of blog posts about numerical algorithms in <code class="language-plaintext highlighter-rouge">matrico</code>.
Next, I will discuss how to potentially use or bundle CHICKEN.</p>Christian Himpematrico’s Differential Equation SolversMatrico Algorithms II2023-08-21T00:00:00+00:002023-08-21T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2023/08/21/matrico-algorithms-ii<h2 id="matricos-qr-decomposition"><code class="language-plaintext highlighter-rouge">matrico</code>’s QR Decomposition</h2>
<ul>
<li><a href="http://numerical-schemer.xyz/2023/07/10/matrico-algorithms-i.html">Part I</a></li>
<li>Part II</li>
<li><a href="http://numerical-schemer.xyz/2023/10/05/matrico-algorithms-iii.html">Part III</a></li>
</ul>
<h3 id="the-significance-of-the-qr-decomposition">The Significance of the QR Decomposition</h3>
<p>In essence, the <a href="https://en.wikipedia.org/wiki/QR_decomposition">QR decomposition</a>
aka QR factorization is a (multiplicative) matrix decomposition,
meaning, it is an algorithm that, given a matrix <code class="language-plaintext highlighter-rouge">A</code>, yields matrices <code class="language-plaintext highlighter-rouge">Q</code> and <code class="language-plaintext highlighter-rouge">R</code>
such that <code class="language-plaintext highlighter-rouge">A = Q R</code>. The factors <code class="language-plaintext highlighter-rouge">Q</code> and <code class="language-plaintext highlighter-rouge">R</code> feature properties that are useful
for higher level algorithms, described next. The defining quality of this decomposition is,
that the <code class="language-plaintext highlighter-rouge">Q</code> factor is <a href="https://en.wikipedia.org/wiki/Orthogonal_matrix">orthogonal</a>,
and the <code class="language-plaintext highlighter-rouge">R</code> factor is <a href="https://en.wikipedia.org/wiki/Triangular_matrix">upper right triangular</a>.</p>
<p>A central application of the QR decomposition is solving linear systems:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A x = b
</code></pre></div></div>
<p>Here the orthogonality of <code class="language-plaintext highlighter-rouge">Q</code> is exploited by using that the inverse of it is
given by its transpose (for a square <code class="language-plaintext highlighter-rouge">Q</code>, a rectangular <code class="language-plaintext highlighter-rouge">Q</code> has a related feature),
which hence can be applied easily to the right-hand side vector <code class="language-plaintext highlighter-rouge">b</code>. Now, <code class="language-plaintext highlighter-rouge">R</code> can be
inversely applied to <code class="language-plaintext highlighter-rouge">b</code> by <a href="https://en.wikipedia.org/wiki/Triangular_matrix#Forward_and_back_substitution">forward substitution</a>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A x = Q R x = b => R x = Q^-1 b = Q^T b => x = R^-1 (Q^T b)
</code></pre></div></div>
<p>The QR decomposition can also be used to compute a matrix’s <a href="https://en.wikipedia.org/wiki/Eigendecomposition_of_a_matrix">eigenvalue decomposition</a>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A = S^-1 D S
</code></pre></div></div>
<p>via the <a href="https://en.wikipedia.org/wiki/QR_algorithm">Francis QR algorithm</a>, one of the <a href="https://web.archive.org/web/20210507011904/https://pi.math.cornell.edu/~web6140/TopTenAlgorithms/QRalgorithm.html">Top Ten Algorithms</a>,
which is a variant of the <a href="https://en.wikipedia.org/wiki/Power_iteration">Power Iteration</a>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A_0 = A
A_k = Q_k R_k
A_k+1 = R_k Q_k = Q_k^-1 Q_k R_k Q_k = Q_k^T A_k Q_k
</code></pre></div></div>
<p>A <a href="https://en.wikipedia.org/wiki/Singular_value_decomposition">singular value decomposition</a>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A = U D V^T
</code></pre></div></div>
<p>in turn can be computed <a href="https://math.stackexchange.com/a/4309570/29967">via the QR algorithm</a>,
and thus by extension via the QR decomposition by computing the eigenvalue
decompositions of the matrix products <code class="language-plaintext highlighter-rouge">A A^T</code> and <code class="language-plaintext highlighter-rouge">A^T A</code>.</p>
<p>The determinant of a matrix can be (approximately, up to sign) computed by the QR decomposition, too:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>A = Q R => det(A) = det(Q) det(R) = ±1 det(R) = ±Π_i R_ii => |det(A)| = |Π_i R_ii|
</code></pre></div></div>
<h3 id="computing-the-qr-decomposition">Computing the QR Decomposition</h3>
<p>It is critical to understand that there is no one algorithm called <strong>the</strong> QR decomposition,
but a family of algorithms which in exact arithmetic are equivalent, but differ
in accuracy, efficiency and overall complexity.
Now, at the core lies the choice of the orthogonalization algorithm, of which the
top three are:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Householder_transformation">Householder Reflections</a></li>
<li><a href="https://en.wikipedia.org/wiki/Givens_rotation">Givens Rotations</a></li>
<li><a href="https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process">Gram-Schmidt</a></li>
</ul>
<p><code class="language-plaintext highlighter-rouge">matrico</code> is using the Gram-Schmidt orthogonalization,
because it works vector-wise and not mainly element-wise as the other two methods.
Now, there are two major variants of Gram Schmidt:</p>
<ul>
<li>(Classic) Gram-Schmidt</li>
<li>Modified Gram-Schmidt</li>
</ul>
<p><code class="language-plaintext highlighter-rouge">matrico</code> uses the modified variant as it is <a href="https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process#Numerical_stability">numerically stabilized</a> and thus more accurate in finite precision arithmetic.
Lastly, the orthogonalization can be performed:</p>
<ul>
<li>row-wise</li>
<li>column-wise</li>
</ul>
<p>Since, <code class="language-plaintext highlighter-rouge">matrico</code>’s matrix layout is as a list of columns, the column-wise approach is chosen.</p>
<p>Altogether, computing the QR via column-wise modified Gram-Schmidt is called “Schwarz-Rutishauser”,
of which I learned about from in <a href="https://towardsdatascience.com/can-qr-decomposition-be-actually-faster-schwarz-rutishauser-algorithm-a32c0cde8b9b">this</a> post.</p>
<h3 id="matricos-qr-decomposition-1"><code class="language-plaintext highlighter-rouge">matrico</code>’s QR Decomposition</h3>
<p><code class="language-plaintext highlighter-rouge">matrico</code> implements a hierarchy of six functions related to the QR decomposition,
each for a particular use-case.
Underlying all is the <code class="language-plaintext highlighter-rouge">mx-qr</code> function:</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">mx-qr</span> <span class="nv">mat</span> <span class="o">.</span> <span class="nv">rev</span><span class="p">)</span>
<span class="p">(</span><span class="k">define</span> <span class="nv">qrows</span> <span class="p">(</span><span class="nf">matrix-rows</span> <span class="nv">mat</span><span class="p">))</span>
<span class="p">(</span><span class="k">define</span> <span class="nv">rrows</span> <span class="p">(</span><span class="nf">fxmin</span> <span class="p">(</span><span class="nf">matrix-cols</span> <span class="nv">mat</span><span class="p">)</span> <span class="nv">qrows</span><span class="p">))</span>
<span class="p">(</span><span class="k">let</span> <span class="nv">rho</span> <span class="p">[(</span><span class="nf">Ak</span> <span class="p">(</span><span class="nf">matrix-explode</span> <span class="nv">mat</span><span class="p">))</span>
<span class="p">(</span><span class="nf">Q</span> <span class="nv">nil</span><span class="p">)</span>
<span class="p">(</span><span class="nf">R</span> <span class="nv">nil</span><span class="p">)]</span>
<span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">empty?</span> <span class="nv">Ak</span><span class="p">)</span> <span class="p">(</span><span class="nb">cons</span> <span class="p">(</span><span class="nf">matrix-implode</span> <span class="nv">Q</span><span class="p">)</span> <span class="p">(</span><span class="nf">matrix-implode</span> <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">optional</span> <span class="nv">rev</span> <span class="no">#t</span><span class="p">)</span> <span class="p">(</span><span class="nb">reverse</span> <span class="nv">R</span><span class="p">)</span> <span class="nv">R</span><span class="p">)))</span>
<span class="p">(</span><span class="k">let</span> <span class="p">[(</span><span class="nf">Rk</span> <span class="p">(</span><span class="nf">make-matrix*</span> <span class="nv">rrows</span> <span class="mi">1</span> <span class="mf">0.0</span><span class="p">))]</span>
<span class="p">(</span><span class="nf">rho</span> <span class="p">(</span><span class="nf">tail</span> <span class="nv">Ak</span><span class="p">)</span>
<span class="p">(</span><span class="k">let</span> <span class="nv">cmgs</span> <span class="p">[(</span><span class="nf">i</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="nf">Qk</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">Ak</span><span class="p">))</span>
<span class="p">(</span><span class="nf">Qi</span> <span class="nv">Q</span><span class="p">)]</span>
<span class="p">(</span><span class="k">cond</span> <span class="p">[(</span><span class="nf">fx>=</span> <span class="nv">i</span> <span class="nv">qrows</span><span class="p">)</span> <span class="nv">Q</span><span class="p">]</span>
<span class="p">[(</span><span class="nf">empty?</span> <span class="nv">Qi</span><span class="p">)</span> <span class="p">(</span><span class="nf">append*</span> <span class="nv">Q</span> <span class="p">(</span><span class="nf">mx/</span> <span class="nv">Qk</span> <span class="p">(</span><span class="nf">matrix-set!</span> <span class="nv">Rk</span> <span class="nv">i</span> <span class="mi">0</span> <span class="p">(</span><span class="nf">mx-norm</span> <span class="nv">Qk</span> <span class="ss">'fro</span><span class="p">))))]</span>
<span class="p">[</span><span class="k">else</span> <span class="p">(</span><span class="nf">cmgs</span> <span class="p">(</span><span class="nf">fx+1</span> <span class="nv">i</span><span class="p">)</span> <span class="p">(</span><span class="nf">mx-axpy</span> <span class="p">(</span><span class="nf">fpneg</span> <span class="p">(</span><span class="nf">matrix-set!</span> <span class="nv">Rk</span> <span class="nv">i</span> <span class="mi">0</span> <span class="p">(</span><span class="nf">matrix-scalar</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">Qi</span><span class="p">)</span> <span class="nv">Qk</span><span class="p">)))</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">Qi</span><span class="p">)</span> <span class="nv">Qk</span><span class="p">)</span> <span class="p">(</span><span class="nf">tail</span> <span class="nv">Qi</span><span class="p">))]))</span>
<span class="p">(</span><span class="nb">cons</span> <span class="nv">Rk</span> <span class="nv">R</span><span class="p">))))))</span>
</code></pre></div></div>
<p>It consists of a tail-call recursion over all columns of the input matrix <code class="language-plaintext highlighter-rouge">A</code>,
in which the another recursion (<code class="language-plaintext highlighter-rouge">cgms</code> <strong>C</strong>olumn-wise <strong>M</strong>odified <strong>G</strong>ram-<strong>S</strong>chmidt) over all currently orthogonal columns of <code class="language-plaintext highlighter-rouge">Q</code> runs, that in turn computes an inner product to obtain the necessary projections,
and yields the to be expected cubic complexity.</p>
<p>This function is meant to be used if the <code class="language-plaintext highlighter-rouge">Q</code> and <code class="language-plaintext highlighter-rouge">R</code> matrices are required explicitly,
for example for use in higher-level algorithms, like the Francis-QR algorithm.</p>
<p>In case linear systems for one linear operator (matrix) <code class="language-plaintext highlighter-rouge">A</code> but many different right-hand sides <code class="language-plaintext highlighter-rouge">b_i</code>
are supposed to be solved, the function <code class="language-plaintext highlighter-rouge">mx-solver</code> first computes a QR decomposition via <code class="language-plaintext highlighter-rouge">mx-qr</code> and
then returns a (closure) function that if called with a suitable vector performs forward substitution.
Also, I note that the optional boolean argument <code class="language-plaintext highlighter-rouge">rev</code> of <code class="language-plaintext highlighter-rouge">mx-qr</code> is only supposed to be used by this
function, as it can save considerable work for large matrices.</p>
<p>If only one linear system needs solving the all-in-one function <code class="language-plaintext highlighter-rouge">mx-solve</code>,
expecting a matrix and a vector, is available.</p>
<p>Another convenience wrapper is the function <code class="language-plaintext highlighter-rouge">mx-orth</code> which just computes the <code class="language-plaintext highlighter-rouge">Q</code> factor of a matrix.
To compute the absolute value of the determinant,
the function <code class="language-plaintext highlighter-rouge">mx-absdet</code> computes the product of diagonal values of the factor <code class="language-plaintext highlighter-rouge">R</code>;
and for positive-definite matrices, the <code class="language-plaintext highlighter-rouge">mx-logdet</code> function computes the logarithm of the determinat
as the sum of logarithms of <code class="language-plaintext highlighter-rouge">R</code>’s diagonal.</p>
<p>Finally, if you want to learn more about the Gram-Schmidt algorithm, I recommend
the review paper “<a href="https://doi.org/10.1002/nla.1839">Gram-Schmidt orthogonalization: 100 years and more</a>”,
of which you can find non-paywalled documents via <a href="https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=intitle%3A%22Gram-Schmidt+orthogonalization%3A+100+years+and+more%22&btnG=">google scholar</a>.
In the final part of this series, I will layout <strong>matrico</strong>’s time stepping solvers.</p>Christian Himpematrico’s QR DecompositionMatrico Algorithms I2023-07-10T00:00:00+00:002023-07-10T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2023/07/10/matrico-algorithms-i<h2 id="matricos-matrix-multiplication"><code class="language-plaintext highlighter-rouge">matrico</code>’s Matrix Multiplication</h2>
<ul>
<li>Part I</li>
<li><a href="http://numerical-schemer.xyz/2023/08/21/matrico-algorithms-ii.html">Part II</a></li>
<li><a href="http://numerical-schemer.xyz/2023/10/05/matrico-algorithms-iii.html">Part III</a></li>
</ul>
<h3 id="significance-of-matrix-multiplication">Significance of Matrix Multiplication</h3>
<p><a href="https://en.wikipedia.org/wiki/Matrix_multiplication">Matrix multiplication</a>
is not only the abstract mathematical operation producing the matrix product,
it has application in many sciences.
Also, it can be interpreted as system of linear equations,
<a href="https://en.wikipedia.org/wiki/Function_composition">composition</a> of linear maps,
as well as all possible <a href="https://en.wikipedia.org/wiki/Dot_product">inner products</a> of sets of column vectors,
or (since a vector can be interpreted as a matrix) as plain matrix-vector multiplication -
the application of a linear map to a vector.</p>
<p>The classical algorithm has a cubic compute complexity for two square matrices of the same dimensions.
However, two notes need to be made:
First, as data can be reused, the memory complexity is only quadratic.
Second, the practical efficency highly depends on cache awareness of the nested iteration (recursion);
see “<a href="https://github.com/divakarvi/bk-spca">Scientific Programming and Computer Architecture</a>” for details.
Lastly, I note there are compute-wise more efficient algorithms with complexities below cubic, such as Strassen’s,
as well as memory-wise more efficient algorithms such blocked (tiled) algorithms,
but either are outside the scope of <strong>matrico</strong> and this post.</p>
<h3 id="the-mathematical-algorithm">The Mathematical Algorithm</h3>
<p>Given two matrices <code class="language-plaintext highlighter-rouge">X</code> and <code class="language-plaintext highlighter-rouge">Y</code>, an element-wise computation of the matrix product <code class="language-plaintext highlighter-rouge">Z</code> can be described by:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X ∈ ℝᴹ×ᴷ, Y ∈ ℝᴷ×ᴺ
X ⋅ Y := Z ∈ ℝᴹ×ᴺ : Zₘₙ = ∑ₖ₌₁ᴷ Xₘₖ Yₖₙ
</code></pre></div></div>
<p>For the two matrices to be compatible for matrix multiplication,
the first operand’s (<code class="language-plaintext highlighter-rouge">X</code>) column dimension <code class="language-plaintext highlighter-rouge">K</code> must match the second operand’s (<code class="language-plaintext highlighter-rouge">Y</code>) row dimension <code class="language-plaintext highlighter-rouge">K</code>.
The resulting matrix <code class="language-plaintext highlighter-rouge">Z</code> will have the row dimension of the first operand <code class="language-plaintext highlighter-rouge">M</code>,
and the column dimension of the second operand <code class="language-plaintext highlighter-rouge">N</code>.
Practically, the inner product of the <code class="language-plaintext highlighter-rouge">m</code>-th row of <code class="language-plaintext highlighter-rouge">X</code> with the <code class="language-plaintext highlighter-rouge">n</code>-th column of <code class="language-plaintext highlighter-rouge">Y</code>
yields the <code class="language-plaintext highlighter-rouge">(m,n)</code> element of <code class="language-plaintext highlighter-rouge">Z</code>.</p>
<h3 id="matricos-implementation"><code class="language-plaintext highlighter-rouge">matrico</code>’s Implementation</h3>
<p>To maximize the use of <strong>matrico</strong>’s matrix data structure,
the base matrix multiplication algorithm is designed for the product of two matrices,
where the first argument is passed as transposition of the actual matrix,
which means the sequential memory columns can exploited for both operands.
In other words: The matrix product of matrices <code class="language-plaintext highlighter-rouge">X</code> and <code class="language-plaintext highlighter-rouge">Y</code> is computed, given <code class="language-plaintext highlighter-rouge">X</code>-transposed and <code class="language-plaintext highlighter-rouge">Y</code>.
And, the arguments are thus compatible for matrix multiplication if their numbers of rows agree.
In mathematical terms we slightly reformulate the above formula to:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>X ⋅ Y := Z ∈ ℝᴹ×ᴺ : Zₘₙ = ∑ₖ₌₁ᴷ Xᵀₖₘ Yₖₙ
</code></pre></div></div>
<p>In (CHICKEN) Scheme code this is implemented by the internal function <a href="https://github.com/gramian/matrico/blob/main/src/matrix.scm#L332"><code class="language-plaintext highlighter-rouge">matrix-dot*</code></a>:</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">matrix-dot*</span> <span class="nv">xt</span> <span class="nv">y</span><span class="p">)</span>
<span class="p">(</span><span class="k">define</span> <span class="nv">xt-data</span> <span class="p">(</span><span class="nf">matrix-data</span> <span class="nv">xt</span><span class="p">))</span>
<span class="p">(</span><span class="k">define</span> <span class="nv">xt-cols</span> <span class="p">(</span><span class="nf">matrix-cols</span> <span class="nv">xt</span><span class="p">))</span>
<span class="p">(</span><span class="nf">matrix-map*</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y-col</span><span class="p">)</span>
<span class="p">(</span><span class="k">let</span> <span class="p">[(</span><span class="nf">ret</span> <span class="p">(</span><span class="nf">make-column</span> <span class="nv">xt-cols</span><span class="p">))]</span>
<span class="p">(</span><span class="k">let</span> <span class="nv">rho</span> <span class="p">[(</span><span class="nf">idx</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">(</span><span class="nf">lst</span> <span class="nv">xt-data</span><span class="p">)]</span>
<span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nf">fx=</span> <span class="nv">idx</span> <span class="nv">xt-cols</span><span class="p">)</span> <span class="nv">ret</span>
<span class="p">(</span><span class="k">begin</span>
<span class="p">(</span><span class="nf">column-set!</span> <span class="nv">ret</span> <span class="nv">idx</span> <span class="p">(</span><span class="nf">column-dot</span> <span class="p">(</span><span class="nf">head</span> <span class="nv">lst</span><span class="p">)</span> <span class="nv">y-col</span><span class="p">))</span>
<span class="p">(</span><span class="nf">rho</span> <span class="p">(</span><span class="nf">fx+1</span> <span class="nv">idx</span><span class="p">)</span> <span class="p">(</span><span class="nf">tail</span> <span class="nv">lst</span><span class="p">)))))))</span>
<span class="nv">y</span><span class="p">))</span>
</code></pre></div></div>
<p>where the star <code class="language-plaintext highlighter-rouge">*</code> signifies that this is not the standard matrix multiplication,
but with the first argument transposed.
First, local bindings are set up for the data element (the list of vectors) of the first (transposed) argument,
and its number of columns.
Then, making up the cubic complexity, follow the three nested loops:</p>
<ol>
<li>A <code class="language-plaintext highlighter-rouge">matrix-map*</code> (map over all columns) of the second argument matrix.</li>
<li>A “manual” <code class="language-plaintext highlighter-rouge">for-each</code> column of the first argument matrix (manual recursion is used to track the index).</li>
<li>The inner product of the current column of the first argument with current column of the second argument,</li>
</ol>
<p>A more functional way would be to assemble a list of resulting inner product results in the middle loop,
and then convert the list via <code class="language-plaintext highlighter-rouge">list->column</code> (encapsuling <code class="language-plaintext highlighter-rouge">list->f64vector</code>) to the vector type on return,
but this would have a significant performance impact compared to the mutating the current result column elements.</p>
<p>All in all, for the <strong>matrico</strong> user this means when using matrix multiplication,
prefer the use of <a href="http://wiki.call-cc.org/eggref/5/matrico#matrix-multiplication"><code class="language-plaintext highlighter-rouge">mx-dot*</code></a> as it is the fastest, directly using <code class="language-plaintext highlighter-rouge">matrix-dot*</code>;
<code class="language-plaintext highlighter-rouge">mx-dot</code> is slower as it needs to transpose the first argument before applying <code class="language-plaintext highlighter-rouge">mx-dot*</code>.</p>
<p>Next, I will dissect the QR algorithm and <strong>matrico</strong>’s implementation of it.</p>Christian Himpematrico’s Matrix MultiplicationCHICKEN Metadata2023-06-14T00:00:00+00:002023-06-14T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2023/06/14/chicken-metadata<h2 id="scheme-based-software-package-manifests">Scheme-Based Software Package Manifests</h2>
<p>One of the highlights of CHICKEN Scheme is it’s “egg” (community) <a href="http://eggs.call-cc.org/5/">extension repository</a>.
To describe such an extension to the repository and hence to a potential user,
information about its contents need to be assigned (or deducted).
This information about code (which is data after all) is metadata.
The selection and detail of metadata assigned to a software package in a repository is a task not unique to CHICKEN though.
In this post, I look at the <a href="http://wiki.call-cc.org/man/5/Egg%20specification%20format">CHICKEN Scheme egg vocabulary</a> and its closest relatives - Scheme(-based) package manifest formats:</p>
<ul>
<li><a href="https://snow-fort.org/doc/spec/">Snow</a></li>
<li><a href="https://gitlab.com/akkuscm/akku/-/blob/master/Akku.manifest">Akku</a></li>
<li><a href="http://synthcode.com/scheme/common-scheme/doc/common-scheme-Z-H-6.html#node_sec_6.3.10">Common Scheme</a></li>
<li><a href="https://guix.gnu.org/en/cookbook/en/html_node/A-_0060_0060Hello-World_0027_0027-package.html">Guix</a></li>
</ul>
<p>As a disclaimer, I will note that only a subset of the respective vocabularies is discussed.</p>
<h3 id="chicken-egg">CHICKEN egg</h3>
<p>The egg specification format is an association list where the keys (symbols) are defined values.
Interestingly, an egg can contain multiple modules, libraries, programs, scripts or data files.
This is not obvious as one could assume only a single module is encapsuled by an egg.
The name of the egg is determined by the <a href="http://wiki.call-cc.org/man/5/Extensions#creating-eggs">filename</a>,
thus there is no <code class="language-plaintext highlighter-rouge">name</code> field needed inside the egg.</p>
<p>Below is a variant of the actual <code class="language-plaintext highlighter-rouge">matrico</code> egg file (named <code class="language-plaintext highlighter-rouge">matrico.egg</code>):</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="nf">version</span> <span class="s">"0.0"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">author</span> <span class="s">"Christian Himpe"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">synopsis</span> <span class="s">"A flonum matrix module for CHICKEN Scheme."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">license</span> <span class="s">"ZLIB-Acknowledgement"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">category</span> <span class="nv">math</span><span class="p">)</span>
<span class="p">(</span><span class="nf">components</span> <span class="p">(</span><span class="nf">extension</span> <span class="nv">matrico</span> <span class="p">(</span><span class="nf">csc-options</span> <span class="s">"-O3"</span> <span class="s">"-d0"</span> <span class="s">"-C"</span> <span class="s">"-O2"</span><span class="p">))))</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">version</code>-value in CHICKEN eggs is of type string, which is suggested to be of the form <code class="language-plaintext highlighter-rouge">"major.minor.patch"</code>,
which allows it to be compared based on split <a href="http://api.call-cc.org/5/doc/semantic-version/version-compare">string comparisons</a>.
Yet, the egg’s defined version matters only for <a href="http://wiki.call-cc.org/man/5/Egg%20specification%20format#version">installing eggs locally</a>,
as the actual version is set by the <a href="http://wiki.call-cc.org/releasing-your-egg#creating-a-release-info-file"><code class="language-plaintext highlighter-rouge">release-info</code> file</a>.</p>
<p>An interesting aspect of the CHICKEN egg metadata-set is the <code class="language-plaintext highlighter-rouge">category</code>-key,
which allows a topical grouping of extensions.
The associated <code class="language-plaintext highlighter-rouge">category</code>-values have to be from a set of <a href="http://wiki.call-cc.org/eggs%20tutorial#egg-categories">internally defined values</a>,
to ensure its validity.</p>
<p>As a sidenote, the <code class="language-plaintext highlighter-rouge">components</code> value describes how a specific part of the egg is build, here the <code class="language-plaintext highlighter-rouge">matrico</code> <strong>module</strong>.
The remaining keys are discussed in relation to contenders summarized in the following.</p>
<h3 id="snow-package">Snow Package</h3>
<p><code class="language-plaintext highlighter-rouge">Snow</code> is an implementation independent package repository for R7RS codes.
Here is how a manifest for a <code class="language-plaintext highlighter-rouge">matrico</code> <code class="language-plaintext highlighter-rouge">Snow</code> package could look:</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">package</span>
<span class="p">(</span><span class="nf">name</span> <span class="p">(</span><span class="nf">matrico</span><span class="p">))</span>
<span class="p">(</span><span class="nf">version</span> <span class="s">"0.0"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">authors</span> <span class="s">"Christian Himpe"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">description</span> <span class="s">"A flonum matrix module for CHICKEN Scheme."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">license</span> <span class="ss">'zlib-acknowledgement</span><span class="p">)</span>
<span class="p">(</span><span class="nf">manual</span> <span class="s">"README.md"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">test</span> <span class="s">"tests/run.scm"</span><span class="p">))</span>
</code></pre></div></div>
<h3 id="akku-package">Akku Package</h3>
<p><code class="language-plaintext highlighter-rouge">Akku.scm</code> is a package manager for R6RS codes and a mirror of the <code class="language-plaintext highlighter-rouge">Snow</code> repository providing R7RS packages.
Following is a prototypical <code class="language-plaintext highlighter-rouge">Akku</code> manifest for <code class="language-plaintext highlighter-rouge">matrico</code>:</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">akku-package</span> <span class="p">(</span><span class="s">"matrico"</span> <span class="s">"0.0"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">authors</span> <span class="s">"Christian Himpe"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">synopsis</span> <span class="s">"A flonum matrix module for CHICKEN Scheme."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">description</span> <span class="s">"A flonum matrix module for CHICKEN Scheme."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">license</span> <span class="s">"ZLIB-Acknowledgement"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">homepage</span> <span class="s">"http://numerical-schemer.xyz"</span><span class="p">))</span>
</code></pre></div></div>
<h3 id="common-scheme-module">Common Scheme Module</h3>
<p><code class="language-plaintext highlighter-rouge">Common Scheme</code> is a portable module system for various Schemes.
In contrast to the other analyzed metadata formats,
here the metadata is embedded in the module definition.
Notably, <code class="language-plaintext highlighter-rouge">Common Scheme</code>’s <code class="language-plaintext highlighter-rouge">common-module</code> do not define a set of valid keys except <code class="language-plaintext highlighter-rouge">summary</code> and <code class="language-plaintext highlighter-rouge">description</code>.
Below is a dummy <code class="language-plaintext highlighter-rouge">matrico</code> <code class="language-plaintext highlighter-rouge">common-module</code> definition using the suggested declarations:</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">common-module</span> <span class="p">(</span><span class="nf">matrico</span><span class="p">)</span>
<span class="p">((</span><span class="nf">export</span> <span class="o">...</span><span class="p">)</span>
<span class="p">(</span><span class="nf">version</span> <span class="s">"0.0"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">author</span> <span class="s">"Christian Himpe"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">summary</span> <span class="s">"A flonum matrix module for CHICKEN Scheme."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">description</span> <span class="s">"..."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">license</span> <span class="s">"ZLIB-Acknowledgement"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">homepage</span> <span class="s">"http://numerical-schemer.xyz"</span><span class="p">)))</span>
</code></pre></div></div>
<h3 id="guix-package">Guix Package</h3>
<p><code class="language-plaintext highlighter-rouge">Guix</code> is a <a href="https://en.wikipedia.org/wiki/GNU_Guix">functional package manager</a> with a Scheme-based package definition.
An incomplete definition for a <code class="language-plaintext highlighter-rouge">matrico</code> <code class="language-plaintext highlighter-rouge">Guix</code> package follows:</p>
<div class="language-scheme highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">define-public</span> <span class="nv">matrico</span>
<span class="p">(</span><span class="nf">package</span>
<span class="p">(</span><span class="nf">name</span> <span class="s">"matrico"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">version</span> <span class="s">"0.0"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">synopsis</span> <span class="s">"A flonum matrix module for CHICKEN Scheme."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">description</span> <span class="s">"..."</span><span class="p">)</span>
<span class="p">(</span><span class="nf">license</span> <span class="nv">zlib-acknowledgement</span><span class="p">)</span>
<span class="p">(</span><span class="nf">home-page</span> <span class="s">"http://numerical-schemer.xyz"</span><span class="p">)</span>
<span class="p">(</span><span class="nf">source</span> <span class="o">...</span><span class="p">)))</span>
</code></pre></div></div>
<h3 id="compared-to-chicken-egg">Compared to CHICKEN egg</h3>
<p>First, common among all compared vocabularies are fields for “version”, “author(s)”, “synopsis/summary/description”, and “license”.</p>
<p>The <code class="language-plaintext highlighter-rouge">author</code>-value in CHICKEN is a string.
For multiple authors, I think a list of strings would be preferrable as in the <code class="language-plaintext highlighter-rouge">Snow</code> manifest.
This will require more logic analyzing the <code class="language-plaintext highlighter-rouge">author</code>-value, as it can now be of type string for a single author, or a list of strings for multiple authors.</p>
<p>For CHICKEN eggs the <code class="language-plaintext highlighter-rouge">license</code>-value is a string, which gives maximal flexibility to specify a license,
for example as an SPDX identifier, a URL to the license, or a free form text.
I also assume a string is used, so <a href="http://wiki.call-cc.org/eggs-licensing#egg-metafile-tagging">multiple licenses may be named</a>.
Using a defined value like <code class="language-plaintext highlighter-rouge">Guix</code> means maximal restriction, as only the exact predefined names may be used.
<code class="language-plaintext highlighter-rouge">Snow</code>’s symbol-type values for licenses seems to be a good compromise.
For multiple licenses a list of symbols, instead of a single symbol, could then be used, as for the author field.</p>
<p>Particularly interesting in the Common Scheme metadata is the use of the <code class="language-plaintext highlighter-rouge">summary</code>-value (and if not available the <code class="language-plaintext highlighter-rouge">description</code>-value).
This value is used to populate an index file with the module descriptions to improve searchability.
A related idea is used in <a href="https://www.mathworks.com/help/matlab/matlab_prog/add-help-for-your-program.html">MATLAB for providing help</a> in the terminal.
The use of the <code class="language-plaintext highlighter-rouge">synopsis</code>-value for an interpreter help would be a nice addition to the interpreter.
For example one could add a toplevel special command like <code class="language-plaintext highlighter-rouge">,y EGGNAME</code> which prints the egg’s synopsis.</p>
<p>The <code class="language-plaintext highlighter-rouge">Akku</code>, <code class="language-plaintext highlighter-rouge">Common Scheme</code>, and <code class="language-plaintext highlighter-rouge">Guix</code> vocabularies provide a <code class="language-plaintext highlighter-rouge">homepage</code> (or <code class="language-plaintext highlighter-rouge">home-page</code>) key-value pair.
At first-glance, this seems to be missing from the CHICKEN vocabulary.
However, there are two reasons making this information not essential for CHICKEN:
First, each egg has its own wiki page, so a potential homepage (or rather website) can be noted there,
and second, such a key would introduce ambivalence between the homepage and the egg wiki page.</p>
<p>The <code class="language-plaintext highlighter-rouge">Snow</code> vocabulary has a <code class="language-plaintext highlighter-rouge">manual</code> key-value pair.
Similar to <code class="language-plaintext highlighter-rouge">homepage</code>, this is of lesser use for CHICKEN due to the online egg documentation.
However, pointing to a text file that could be installed with the egg files as offline documentation could be useful.</p>
<p>Summing up, CHICKEN eggs provide all typical metadata fields,
but in my humble opinion the <code class="language-plaintext highlighter-rouge">author</code> and <code class="language-plaintext highlighter-rouge">license</code> fields could be improved.
The documentation of the CHICKEN egg metadata is definitely the best among the compared formats,
with the minor remark that it does not state minimally required keys (as do neither of the other discussed formats’ docs).
But overall, the egg format feels well thought-out and useful.</p>
<p>I may update this post with further insights,
but next, in a trilogy of posts, I will explain high complexity algorithms in <code class="language-plaintext highlighter-rouge">matrico</code>.</p>Christian HimpeScheme-Based Software Package ManifestsMatrico Module V2022-12-15T00:00:00+00:002022-12-15T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2022/12/15/matrico-matrico-v<h2 id="matricos-matrico-module-part-v"><code class="language-plaintext highlighter-rouge">matrico</code>’s <code class="language-plaintext highlighter-rouge">matrico</code> Module (Part V)</h2>
<ul>
<li><a href="http://numerical-schemer.xyz/2022/09/16/matrico-matrico-i.html">Part I</a></li>
<li><a href="http://numerical-schemer.xyz/2022/09/30/matrico-matrico-ii.html">Part II</a></li>
<li><a href="http://numerical-schemer.xyz/2022/10/17/matrico-matrico-iii.html">Part III</a></li>
<li><a href="http://numerical-schemer.xyz/2022/11/30/matrico-matrico-iiii.html">Part IIII</a></li>
<li>Part V</li>
</ul>
<p>In this last part of the five part series on the main <code class="language-plaintext highlighter-rouge">matrico</code> module, I
outline the utility functions as well as the private functions.
These are again contained in the <a href="https://github.com/gramian/matrico/blob/main/src/mx.scm"><code class="language-plaintext highlighter-rouge">src/mx.scm</code></a> Scheme source code file,
that is <strong>included</strong> into the module’s main file <code class="language-plaintext highlighter-rouge">matrico.scm</code>.</p>
<h3 id="utility-functions">Utility Functions</h3>
<p>Up to this post in this series, only functions that instantiate or process matrices were described.
Here, the functions enabling input and output of matrices are described.
In terms of output, <code class="language-plaintext highlighter-rouge">matrico</code>’s matrices can be printed to the REPL console (stdout),
or saved to file, either in <code class="language-plaintext highlighter-rouge">matrico</code>’s native (<a href="https://en.wikipedia.org/wiki/S-expression">s-expression</a>) format or in <a href="https://en.wikipedia.org/wiki/Comma-separated_values">CSV</a> format.
The former directly maps to <code class="language-plaintext highlighter-rouge">matrico</code>’s internal column-major matrix format in serialized form,
the latter is a portable format compatible with <a href="https://www.mathworks.com/help/matlab/ref/readmatrix.html">MATLAB</a>, <a href="https://octave.sourceforge.io/octave/function/csvread.html">Octave</a>, <a href="https://numpy.org/doc/stable/reference/generated/numpy.loadtxt.html">NumPy</a>, <a href="https://csv.juliadata.org/stable/reading.html">Julia</a> or <a href="https://www.rdocumentation.org/packages/utils/versions/3.6.2/topics/read.table">R</a>.
Input-wise, a matrix in <code class="language-plaintext highlighter-rouge">matrico</code>’s format can be loaded from file.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-print mat)</code> - Returns <strong>void</strong>, prints argument <strong>matrix</strong> to stdout.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-export str mat)</code> - Returns <strong>void</strong>, saves argument <strong>matrix</strong> to file at path <strong>string</strong> argument in CSV format.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-save str mat)</code> - Returns <strong>void</strong>, saves argument <strong>matrix</strong> to file in <code class="language-plaintext highlighter-rouge">matrico</code> format at argument <strong>string</strong> path.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-load str)</code> - Returns <strong>matrix</strong> loaded from file in <code class="language-plaintext highlighter-rouge">matrico</code> format at argument <strong>string</strong> path.</li>
</ul>
<h3 id="private-functions">Private Functions</h3>
<p>In the <code class="language-plaintext highlighter-rouge">matrico</code> module, recurring expressions are abstracted into local (non-user-facing) functions.
There are four kinds of abstractions:
predicates testing if an argument is a matrix or flonum,
converters ensuring a matrix or flonum is returned,
converters from one-based frontend indexing allowing positive and negative indices (denoting counting from first entry forward and form last element backward respectively) to zero-based positive-only backend indexing,
and a single-step time stepper for Runge-Kutta method variants.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrix-or-flonum? val)</code> - Returns <strong>boolean</strong> answering if argument is a <strong>matrix</strong> or <strong>flonum</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(scalar-or-flonum? val)</code> - Returns <strong>boolean</strong> answering if argument is a scalar <strong>matrix</strong> or <strong>flonum</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(column-or-flonum? val)</code> - Returns <strong>boolean</strong> answering if argument is a column <strong>matrix</strong> or <strong>flonum</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(ensure-mx val)</code> - Returns <strong>matrix</strong> for <strong>matrix</strong> or <strong>flonum</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(ensure-fp val)</code> - Returns <strong>flonum</strong> for <strong>flonum</strong> or scalar <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(translate-cols mat idx)</code> - Returns <strong>fixnum</strong> zero-based, positive index from one-based, positive/negative column index <strong>fixnum</strong> argument for <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(translate-rows mat idx)</code> - Returns <strong>fixnum</strong> zero-based, positive index from one-based, positive/negative row index <strong>fixnum</strong> argument for <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(time-stepper typ sys tim x0)</code> - Returns <strong>matrix</strong> approximating ordinary differential equation by <strong>procedure</strong> argument method with explicit vector field (and optionally output function) (<strong>pair</strong>-of-)procedure argument over time discretization <strong>pair</strong>-of-<strong>flonum</strong>s argument, and starting at column <strong>matrix</strong> argument initial state.</li>
</ul>
<p>This concludes the summary of the <code class="language-plaintext highlighter-rouge">matrico</code> module.
In the next post I will reflect on CHICKEN Scheme and environment.</p>Christian Himpematrico’s matrico Module (Part V)Matrico Module IIII2022-11-30T00:00:00+00:002022-11-30T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2022/11/30/matrico-matrico-iiii<h2 id="matricos-matrico-module-part-iiii"><code class="language-plaintext highlighter-rouge">matrico</code>’s <code class="language-plaintext highlighter-rouge">matrico</code> Module (Part IIII)</h2>
<p><strong>Series</strong>:</p>
<ul>
<li><a href="http://numerical-schemer.xyz/2022/09/16/matrico-matrico-i.html">Part I</a></li>
<li><a href="http://numerical-schemer.xyz/2022/09/30/matrico-matrico-ii.html">Part II</a></li>
<li><a href="http://numerical-schemer.xyz/2022/10/17/matrico-matrico-iii.html">Part III</a></li>
<li>Part IIII</li>
<li><a href="http://numerical-schemer.xyz/2022/12/15/matrico-matrico-v.html">Part V</a></li>
</ul>
<p>For the fourth part in this series of posts about the main <code class="language-plaintext highlighter-rouge">matrico</code> module of the <code class="language-plaintext highlighter-rouge">matrico</code> project,
I summarize the core numerical functionality.
These are again contained in the <a href="https://github.com/gramian/matrico/blob/main/src/mx.scm"><code class="language-plaintext highlighter-rouge">src/mx.scm</code></a> Scheme source code file,
that is <strong>included</strong> into the module’s main file <code class="language-plaintext highlighter-rouge">matrico.scm</code>.</p>
<h3 id="linear-algebra">Linear Algebra</h3>
<p>Usual basic linear algebra functions that <code class="language-plaintext highlighter-rouge">matrico</code> provides are concatenation of matrices,
<a href="https://en.wikipedia.org/wiki/Vectorization_(mathematics)">vectorization</a> and <a href="https://en.wikipedia.org/wiki/Transpose">transposition</a> of matrices, <a href="https://en.wikipedia.org/wiki/Symmetric_matrix#Decomposition_into_symmetric_and_skew-symmetric">symmetric and anti-symmetric (skew-symmetric) part</a>s of a matrix,
and extracting the <a href="https://en.wikipedia.org/wiki/Diagonal_matrix#Matrix-to-vector_diag_operator">diagonal</a> of a matrix:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-horcat A B)</code> - Returns <strong>matrix</strong> consisting of a copy of horizontally concatenated argument <strong>matrix</strong>es.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-vercat A B)</code> - Returns <strong>matrix</strong> consisting of a copy of vertically concatenated argument <strong>matrix</strong>es.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-vec A)</code> - Returns column-<strong>matrix</strong> with entries of argument <strong>matrix</strong> column-wise stacked.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-transpose A)</code> - Returns <strong>matrix</strong> with entries of argument <strong>matrix</strong> with swapped row and column indices.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-sympart A)</code> - Returns <strong>matrix</strong> being the symmetric part of argument <strong>matrix</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-skewpart A)</code> - Returns <strong>matrix</strong> being the skew-symmetric part of argument <strong>matrix</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-diagonal A)</code> - Returns diagonal <strong>matrix</strong> argument column <strong>matrix</strong>.</li>
</ul>
<h4 id="linear-problems">Linear Problems</h4>
<p>A core competency of numerical linear algebra is the solution of linear problems,
meaning solving a system of linear equations, typically presented in vectorized form
as matrix times unknown vector equals vector: <code class="language-plaintext highlighter-rouge">A x = b</code>.
A class of algorithms to solve such linear problems utilize matrix decompositions.
The idea behind these approaches is to factorize the matrix <code class="language-plaintext highlighter-rouge">A</code> such that the factors
facilitate a solution.
Among the available matrix decomposition algorithms, I selected the <a href="https://en.wikipedia.org/wiki/QR_decomposition">QR decomposition</a> (or QR factorization).
This algorithm factors the matrix <code class="language-plaintext highlighter-rouge">A = QR</code> into an orthogonal matrix <code class="language-plaintext highlighter-rouge">Q</code> and a right triangular matrix <code class="language-plaintext highlighter-rouge">R</code>.
While the former (<code class="language-plaintext highlighter-rouge">Q</code>) can be <a href="https://en.wikipedia.org/wiki/Orthogonal_matrix">inverted by transposition</a>,
the latter (<code class="language-plaintext highlighter-rouge">R</code>) corresponds to <a href="https://en.wikipedia.org/wiki/Triangular_matrix">back substitution</a>;
this means <code class="language-plaintext highlighter-rouge">A = QR => QR x = b => R x = Q^T b</code>.
Advantages of the QR decomposition compared to other decompositions are on the one hand, that the matrix <code class="language-plaintext highlighter-rouge">A</code>
does not need to be symmetric, invertible, or even square (in the singular or rectangular case a <a href="https://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares#Orthogonal_decomposition_methods">least-squares solution</a> is obtained),
on the other hand, the QR decomposition is the basis for more complex algorithms,
such as the <a href="https://en.wikipedia.org/wiki/Singular_value_decomposition#Numerical_approach">Singular Value Decomposition</a> (SVD), or eigenvalue computation via the <a href="https://en.wikipedia.org/wiki/QR_algorithm">Francis-QR algorithm</a>.
Among the various variants of practical QR factorization algorithms, I specifically chose the Gram-Schmidt-based <a href="https://towardsdatascience.com/can-qr-decomposition-be-actually-faster-schwarz-rutishauser-algorithm-a32c0cde8b9b">Schwarz-Rutishauser</a> algorithm,
also known as <a href="https://doi.org/10.1002/nla.1839">column-wise modified Gram Schmidt</a>;
because first, this column-wise algorithm fits <code class="language-plaintext highlighter-rouge">matrico</code>’s matrix data structure,
second, its lower complexity, third, its numerical stability, and lastly its simple implementation.</p>
<p><code class="language-plaintext highlighter-rouge">matrico</code> provides functions computing QR decompositions for different applications,
all using the same core algorithm:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-qr A)</code> - Returns a pair of <strong>matrix</strong>es (Q and R) for an argument <strong>matrix</strong></li>
<li><code class="language-plaintext highlighter-rouge">(mx-solver A)</code> - Returns a <strong>function</strong> (expecting a column matrix argument) that solves the linear problem formed with the argument <strong>matrix</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-solve A b)</code> - Returns a column matrix solving the linear problem formed by argument <strong>matrix</strong> and column <strong>matrix</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-orth A)</code> - Returns <strong>matrix</strong> representing an orthogonalization of argument <strong>matrix</strong>.</li>
</ul>
<p>Use <code class="language-plaintext highlighter-rouge">mx-qr</code> if no linear problem is to be solved, but the factor matrices are required.
Use <code class="language-plaintext highlighter-rouge">mx-solver</code> if multiple linear problems need to be solved with the same matrix <code class="language-plaintext highlighter-rouge">A</code> but different right-hand-sides <code class="language-plaintext highlighter-rouge">b</code>, as the returned function caches the QR decomposition as closure and thus prevents recomputation.
Use <code class="language-plaintext highlighter-rouge">mx-solve</code> if only a single solution to a linear problem is required.</p>
<h4 id="determinants">Determinants</h4>
<p>The QR decomposition also allows to compute a matrix’s <a href="https://en.wikipedia.org/wiki/QR_decomposition#Connection_to_a_determinant_or_a_product_of_eigenvalues">determinant</a> up to its sign.
Thus, the absolute value of the determinant is computable.
Given a <a href="https://en.wikipedia.org/wiki/Definite_matrix">positive definite matrix</a>, also the <a href="https://en.wikipedia.org/wiki/Determinant#Trace">logarithm of the determinant</a> can be computed,
which is useful for badly conditioned matrices.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-absdet A)</code> - Returns the absolute value of the determinant of the argument <strong>matrix</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-logdet A)</code> - Returns the logarithm of the determinant of the argument <strong>matrix</strong>.</li>
</ul>
<h4 id="traces">Traces</h4>
<p>Functions computing accumulated quantites from the matrix diagonal are subsumed under “Traces”.
Usually, a matrix trace refers to the sum of its diagonal values;
here, also a the product of diagonal values is provided,
as well as functions calculating the (additive) trace of a product of matrices,
since the <a href="https://en.wikipedia.org/wiki/Trace_(linear_algebra)#Trace_of_a_product">trace of a product</a> can be computed more efficiently than computing first the product and then its trace.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-trace A)</code> - Returns the sum of diagonal entries of the <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-multrace A)</code> - Returns the product of diagonal entries of the <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-prodtrace A B)</code> - Returns the sum of diagonal entries of the product of <strong>matrix</strong> arguments.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-prodtrace* AT B)</code> - Returns the sum of diagonal entries of the product of <strong>matrix</strong> arguments, with first factor transposed.</li>
</ul>
<h4 id="matrix-multiplication">Matrix multiplication</h4>
<p>Matrix multiplication is also an important pillar of (numerical) linear algebra.
It has interpretations, among others, as compositions of linear transformations,
or all possible inner products of two sets vectors (column matrices).
<code class="language-plaintext highlighter-rouge">matrico</code> provides variants of the generic <a href="https://gramian.github.io/numerical-schemer.xyz/2022/08/04/matrico-matrix.html">matrix multiplication</a>
mainly for convenience:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-scalar AT B)</code> - Returns scalar product (inner product) of column-<strong>matrix</strong> arguments.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-dyadic A B)</code> - Returns dyadic product (outer product) of column-<strong>matrix</strong> argument and row-<strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-dot* AT B)</code> - Returns product of <strong>matrix</strong> arguments with first factor transposed.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-dot A B)</code> - Returns product of <strong>matrix</strong> arguments.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-gram A)</code> - Returns product of transposed <strong>matrix</strong> argument with itself.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-gram* A)</code> - Returns product of <strong>matrix</strong> argument with its transposed self.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-square A)</code> - Returns product of <strong>matrix</strong> argument with itself.</li>
</ul>
<p>The <code class="language-plaintext highlighter-rouge">mx-dot*</code> functions should always be preferred for non-column-matrices, as it is the most performant.</p>
<h3 id="multivariate-statistics">Multivariate Statistics</h3>
<p>Some statistical functions are available in <code class="language-plaintext highlighter-rouge">matrico</code>, too:
<a href="https://en.wikipedia.org/wiki/Cross-covariance_matrix">cross</a>-<a href="https://en.wikipedia.org/wiki/Covariance_matrix">covariance</a>,
<a href="https://en.wikipedia.org/wiki/Cross-correlation_matrix">cross</a>-<a href="https://en.wikipedia.org/wiki/Autocorrelation#Matrix">correlation</a>,
<a href="https://en.wikipedia.org/wiki/Variance">variance</a>, <a href="https://en.wikipedia.org/wiki/Standard_deviation">standard deviation</a>,
<a href="https://en.wikipedia.org/wiki/Cosine_similarity">angle</a>, and <a href="https://en.wikipedia.org/wiki/Coherence_(signal_processing)">coherence</a>.
For these functions the argument matrices are assumed to be arranged as
each row corresponding to all observations of one variable,
while each column represents all variables of one observation:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-xcov A B)</code> - Returns cross-covariance matrix of <strong>matrix</strong> arguments.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-cov A)</code> - Returns covariance of <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-var A)</code> - Returns variance of <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-std A)</code> - Returns standard deviation of <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-xcor A B)</code> - Returns cross-correlation of <strong>matrix</strong> arguments.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-cor A)</code> - Returns correlation of <strong>matrix</strong> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-angle A B)</code> - Returns angles between <strong>matrix</strong> argument columns.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-coher A B)</code> - Returns coherence of <strong>matrix</strong> arguments.</li>
</ul>
<h3 id="analysis-and-calculus">Analysis and Calculus</h3>
<p>Analysis deals with derivatives and integrals,
so numerical analysis approximates derivation and integration.
<code class="language-plaintext highlighter-rouge">matrico</code> provides a function to compute differences of consecutive columns of a matrix,
which can be used for a <a href="https://en.wikipedia.org/wiki/Finite_difference">finite difference</a> approximation of a <a href="https://en.wikipedia.org/wiki/Difference_quotient">difference quotient</a>.
To approximate integrals by <a href="https://en.wikipedia.org/wiki/Riemann_sum">Riemann sums</a>, a column-wise <a href="https://en.wikipedia.org/wiki/Trapezoidal_rule">trapezoid rule</a> is implemented.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-diff A)</code> - Returns <strong>matrix</strong> of differences of consecutive columns of argument <strong>matrix</strong>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-trapz A)</code> - Returns column-<strong>matrix</strong> of sums of consecutive columns of argument <strong>matrix</strong>.</li>
</ul>
<h4 id="ordinary-differential-equations">Ordinary Differential Equations</h4>
<p>A central subject of numerical analysis is the approximation of solutions to differential equations.
<code class="language-plaintext highlighter-rouge">matrico</code> is focusing on <a href="https://en.wikipedia.org/wiki/Ordinary_differential_equation">ordinary differential equations</a> (ODE), differential equations in a one dimensional variable,
as opposed to partial differential equations in multiple dimensional variables.
Ordinary differential equations are prominently used for <a href="https://en.wikipedia.org/wiki/Dynamical_system">dynamical systems</a>,
where the variable is (one-dimensional) time, and describes the temporal evolution of a system.
Integrators, such as <a href="https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods">Runge-Kutta methods</a>,
compute a sequence of point-wise approximations to the solution function’s values, called trajectory for evolution systems.
In <code class="language-plaintext highlighter-rouge">matrico</code> trajectories are obtained by a single step methods, meaning the next (time) step approximation depends only the current approximation step.
Opposed to, for example, <a href="https://www.mathworks.com/help/matlab/math/choose-an-ode-solver.html">MATLAB</a>, <code class="language-plaintext highlighter-rouge">matrico</code> uses fixed step methods,
and not <a href="https://en.wikipedia.org/wiki/Adaptive_step_size">adaptive time step methods</a>.</p>
<p>The two selected solvers implemented in <code class="language-plaintext highlighter-rouge">matrico</code> are explicit Runge-Kutta method of second (nonlinear) order,
and were chosen as they feature low complexity in terms of compute and memory.
Particularly, both methods are of the <a href="https://doi.org/10.1016/j.jcp.2009.11.006">“two register” class (“2R*”)</a>;
this means that internally an iteration requires merely two vector (column-matrix) registers,
of which one is constant for each sub-step iteration.
This is the minimum memory consumption possible.
Furthermore, both time steppers induce a family of methods as either allows to increase the number of sub-steps or stages, to increase stability,
and hence approximate more complicated problems:
First, the family of implemented <a href="https://doi.org/10.1016/0378-4754(84)90056-9">hyperbolic Runge-Kutta methods</a> is aimed at transport, advection, or non-normal problems and require a stability region around the imaginary axis.
These hyperbolic Runge-Kutta methods have the maximum hyperbolic stability limit for an explicit second order method of chosen number of stages.
Second, the family of implemented <a href="https://doi.org/10.1137/07070485X">strong stability preserving Runge-Kutta methods</a> is aimed at general and parabolic problems, which require a stability region in the negative-real half-plane.
Strong stability preserving (SSP) means that if a first order explicit Euler method adheres to some constraint for the given ODE, so will this second-order method, which is optimal among second-order methods in this regard.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-ode2-hyp num sys tim x0)</code> - Returns <strong>matrix</strong> holding trajectory solved by the hyperbolic Runge-Kutta method with <strong>fixnum</strong> argument many stages, <strong>procedure</strong> argument vector field, <strong>pair</strong> argument variable discretization (step, horizon) and column-<strong>matrix</strong> initial value.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-ode2-ssp num sys tim x0)</code> - Returns <strong>matrix</strong> holding trajectory solved by the strong stability preserving Runge-Kutta method with <strong>fixnum</strong> argument many stages, <strong>procedure</strong> argument vector field, <strong>pair</strong> argument variable discretization (step, horizon) and column-<strong>matrix</strong> initial value.</li>
</ul>
<p>Instead of just a vector field also a <strong>pair</strong> of a vector field <strong>procedure</strong> and an output function <strong>procedure</strong> can be passed, which allows to simulate input-output systems, typical in <a href="https://en.wikipedia.org/wiki/Dynamical_systems_theory">system theory</a> and <a href="https://en.wikipedia.org/wiki/Control_theory">control theory</a>.</p>
<p>This sums up the highest-level functionality of <code class="language-plaintext highlighter-rouge">matrico</code>.
The next post will mop up utility and private functions for completeness,
and thereby conclude this series.</p>Christian Himpematrico’s matrico Module (Part IIII)Matrico Module III2022-10-17T00:00:00+00:002022-10-17T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2022/10/17/matrico-matrico-iii<h2 id="matricos-matrico-module-part-iii"><code class="language-plaintext highlighter-rouge">matrico</code>’s <code class="language-plaintext highlighter-rouge">matrico</code> Module (Part III)</h2>
<p><strong>Series</strong>:</p>
<ul>
<li><a href="http://numerical-schemer.xyz/2022/09/16/matrico-matrico-i.html">Part I</a></li>
<li><a href="http://numerical-schemer.xyz/2022/09/30/matrico-matrico-ii.html">Part II</a></li>
<li>Part III</li>
<li><a href="http://numerical-schemer.xyz/2022/11/30/matrico-matrico-iiii.html">Part IIII</a></li>
<li><a href="http://numerical-schemer.xyz/2022/12/15/matrico-matrico-v.html">Part V</a></li>
</ul>
<p>In this third part of the series of posts,
I will describe the various <em>map</em> and <em>reduce</em> specializations provided by the <code class="language-plaintext highlighter-rouge">matrico</code> module.
These are again contained in the <a href="https://github.com/gramian/matrico/blob/main/src/mx.scm"><code class="language-plaintext highlighter-rouge">src/mx.scm</code></a> Scheme source code file,
that is <strong>included</strong> into the module’s main file <code class="language-plaintext highlighter-rouge">matrico.scm</code>.</p>
<h3 id="map-and-reduce">Map and Reduce</h3>
<p>Map and reduce are two of the fundamental operations in <a href="https://en.wikipedia.org/wiki/Functional_programming">functional programming</a>:
The <a href="https://en.wikipedia.org/wiki/Map_(higher-order_function)">map</a> function
applies a given unary function to each element in a container, such as a list, vector or matrix.
The <a href="https://en.wikipedia.org/wiki/Fold_(higher-order_function)">reduce</a> (aka fold) function
applies a given binary function to an accumulator and sequentially each element in a container,
whereas this function’s return value becomes the accumulator for the next evaluation.
These concepts of map and reduce have application beyond functional programming, such as in parallel programming,
and was mainstream popularized by the <a href="https://en.wikipedia.org/wiki/MapReduce">MapReduce</a> programming model.</p>
<p>In <code class="language-plaintext highlighter-rouge">matrico</code>’s <a href="https://numerical-schemer.xyz/2022/08/04/matrico-matrix.html">matrix functor</a>, map and fold functions for <code class="language-plaintext highlighter-rouge">matrico</code>’s matrix type are provided,
which are specialized for typical numerical operations on <code class="language-plaintext highlighter-rouge">flonum</code> matrices in the <code class="language-plaintext highlighter-rouge">matrio</code> module.
These are summarized in three sections:
first the binary <em>expanders</em>, second the unary <em>mappers</em>, and lastly the reducers.</p>
<h3 id="expanders">Expanders</h3>
<p><em>Expanders</em> refer in this context to binary map operations, which allow automatic broadcasting (aka implicit expansion).
This means the two matrix arguments do not need to be of the same dimension,
rather they have to be compatible dimensions, leading to the following five options:</p>
<ul>
<li>Both <code class="language-plaintext highlighter-rouge">matrix</code> arguments have the same number of rows and columns.
The binary map function is applied to the arguments’ respective entries with the same row and column index.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤ ⎡XXX⎤ ⎡XXX⎤
⎢XXX⎥ o ⎢XXX⎥ = ⎢XXX⎥
⎣XXX⎦ ⎣XXX⎦ ⎣XXX⎦
</code></pre></div> </div>
</li>
<li>One <code class="language-plaintext highlighter-rouge">matrix</code> argument is a column matrix, matching the column number of the other argument.
The binary map function is applied as if the column matrix argument had as many columns as the other <code class="language-plaintext highlighter-rouge">matrix</code> arguments with all the same columns.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤ ⎡X⎤ ⎡XXX⎤ ⎡X⎤ ⎡XXX⎤ ⎡XXX⎤
⎢XXX⎥ o ⎢X⎥ = ⎢XXX⎥ , ⎢X⎥ o ⎢XXX⎥ = ⎢XXX⎥
⎣XXX⎦ ⎣X⎦ ⎣XXX⎦ ⎣X⎦ ⎣XXX⎦ ⎣XXX⎦
</code></pre></div> </div>
</li>
<li>One <code class="language-plaintext highlighter-rouge">matrix</code> argument is a row matrix, matching the row number of the other argument.
The binary map function is applied as if the row matrix argument had as many rows as the other <code class="language-plaintext highlighter-rouge">matrix</code> arguments with all the same rows.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤ [XXX] ⎡XXX⎤ [XXX] ⎡XXX⎤ ⎡XXX⎤
⎢XXX⎥ o = ⎢XXX⎥ , o ⎢XXX⎥ = ⎢XXX⎥
⎣XXX⎦ ⎣XXX⎦ ⎣XXX⎦ ⎣XXX⎦
</code></pre></div> </div>
</li>
<li>One <code class="language-plaintext highlighter-rouge">matrix</code> argument is a row matrix, the other is a column matrix.
The binary map function is applied as if the row matrix argument had as many rows as the column matrix arguments with all the same rows, and the column matrix argument had as many columns as the row matrix arguments with all the same columns.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡X⎤ [XXX] ⎡XXX⎤ [XXX] ⎡X⎤ ⎡XXX⎤
⎢X⎥ o = ⎢XXX⎥ , o ⎢X⎥ = ⎢XXX⎥
⎣X⎦ ⎣XXX⎦ ⎣X⎦ ⎣XXX⎦
</code></pre></div> </div>
</li>
<li>One <code class="language-plaintext highlighter-rouge">matrix</code> argument is scalar.
The binary map function is applied as if the scalar matrix or <code class="language-plaintext highlighter-rouge">flonum</code> had as many rows and columns as the other <code class="language-plaintext highlighter-rouge">matrix</code> argument all the same entries.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤ ⎡XXX⎤ ⎡XXX⎤ ⎡XXX⎤
⎢XXX⎥ o X = ⎢XXX⎥ , X o ⎢XXX⎥ = ⎢XXX⎥
⎣XXX⎦ ⎣XXX⎦ ⎣XXX⎦ ⎣XXX⎦
</code></pre></div> </div>
</li>
</ul>
<h4 id="basic-arithmetic">Basic Arithmetic</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx+ x y)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise sums of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
<li><code class="language-plaintext highlighter-rouge">(mx* x y)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise differences of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
<li><code class="language-plaintext highlighter-rouge">(mx- x y)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise products of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
<li><code class="language-plaintext highlighter-rouge">(mx/ x y)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise divisions of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
</ul>
<h4 id="advanced-functions">Advanced Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx^ x y)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise exponentiation of base <code class="language-plaintext highlighter-rouge">matrix</code> argument to exponent <code class="language-plaintext highlighter-rouge">matrix</code> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-where pred x y)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entries of first or second <code class="language-plaintext highlighter-rouge">matrix</code> argument, depending on binary predicate <code class="language-plaintext highlighter-rouge">function</code> argument evaluated on respective <code class="language-plaintext highlighter-rouge">matrix</code> arguments’ entries.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-axpy a x y)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise sums of first <code class="language-plaintext highlighter-rouge">matrix</code> argument, scaled by <code class="language-plaintext highlighter-rouge">flonum</code> argument, with second <code class="language-plaintext highlighter-rouge">matrix</code> argument.</li>
</ul>
<h3 id="mappers">Mappers</h3>
<p><em>Mappers</em> describe an entry-wise processing of a matrix.
The unary map function is applied to each entry yielding a result matrix of the same dimensions.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤ ⎡XXX⎤
⎢XXX⎥ -> ⎢XXX⎥
⎣XXX⎦ ⎣XXX⎦
</code></pre></div></div>
<h4 id="elementary-functions">Elementary Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx- x)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise negation of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
<li><code class="language-plaintext highlighter-rouge">(mx/ x)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise reciprocal of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
<li><code class="language-plaintext highlighter-rouge">(mx*2 x)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise double of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
<li><code class="language-plaintext highlighter-rouge">(mx^2 x)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise square of argument <code class="language-plaintext highlighter-rouge">matrix</code>es entries.</li>
</ul>
<h4 id="rounding-functions">Rounding Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-round mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise roundings of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-floor mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise greatest integer less than or equal to argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-ceil mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of entry-wise least integer greater than or equal to argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="generalized-functions">Generalized Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-abs mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise absolute value of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-sign mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise sign of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-delta mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise Kronecker delta of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-heaviside mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise Heaviside step of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="trigonometric-functions">Trigonometric Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-sin mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise sine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-cos mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise cosine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-tan mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise tangent of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="arcus-functions-inverse-trigonometric-functions">Arcus Functions (Inverse Trigonometric Functions)</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-asin mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise arcsine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-acos mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise arccosine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-atan mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise arctangent of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="hyperbolic-functions">Hyperbolic Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-sinh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise hyperbolic sine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-cosh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise hyperbolic cosine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-tanh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise hyperbolic tangent of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="area-functions-inverse-hyperbolic-functions">Area Functions (Inverse Hyperbolic Functions)</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-asinh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise area hyperbolic sine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-acosh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise area hyperbolic cosine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-atanh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise area hyperbolic tangent of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="haversed-trigonometric-functions">Haversed Trigonometric Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-hsin mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise haversine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-hcos mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise havercosine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="logarithmic-hyperbolic-functions">Logarithmic Hyperbolic Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-lnsinh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise logarithmic hyperbolic sine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-lncosh mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise logarithmic hyperbolic cosine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="squareroots">Squareroots</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-sqrt mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise squareroot of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-signsqrt mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise sign times squareroot of absolute value of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="logarithms">Logarithms</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-ln mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise natural logarithm of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-lb mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise common logarithm of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-lg mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise binary logarithm of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="exponentials">Exponentials</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-exp mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise exponential of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-gauss mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise Gaussian of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h4 id="special-functions">Special Functions</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-sinc mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise cardinale sine of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-sigm mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise sigmoid of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-stirling mat)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being entry-wise Stirling approximation of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h3 id="reducers">Reducers</h3>
<p><em>Reducers</em> compose a matrix’s entries yielding a <code class="language-plaintext highlighter-rouge">matrix</code> of lower dimension.
There are three variants:</p>
<ul>
<li>Composition of row entries yielding a column <code class="language-plaintext highlighter-rouge">matrix</code> by <em>fold</em> ing each row into a scalar.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤ ⎡X⎤
⎢XXX⎥ -> ⎢X⎥
⎣XXX⎦ ⎣X⎦
</code></pre></div> </div>
</li>
<li>Composition of column entries yielding a row <code class="language-plaintext highlighter-rouge">matrix</code> by <em>fold</em> ing each column into a scalar.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤ [XXX]
⎢XXX⎥ ->
⎣XXX⎦
</code></pre></div> </div>
</li>
<li>Composition of all entries yielding a <code class="language-plaintext highlighter-rouge">flonum</code> by <em>fold</em> ing all entries into a scalar.
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⎡XXX⎤
⎢XXX⎥ -> X
⎣XXX⎦
</code></pre></div> </div>
</li>
</ul>
<h4 id="sums">Sums</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-rowsum mat)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of row sums of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-colsum mat)</code> - Returns row <code class="language-plaintext highlighter-rouge">matrix</code> of column sums of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-sum mat)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> sum of all <code class="language-plaintext highlighter-rouge">matrix</code> argument’s entries.</li>
</ul>
<h4 id="products">Products</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-rowprod mat)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of row products of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-colprod mat)</code> - Returns row <code class="language-plaintext highlighter-rouge">matrix</code> of column products of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-prod mat)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> product of all <code class="language-plaintext highlighter-rouge">matrix</code> argument’s etnries.</li>
</ul>
<h4 id="minima">Minima</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-rowmin mat)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of row minima of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-colmin mat)</code> - Returns row <code class="language-plaintext highlighter-rouge">matrix</code> of column minima of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-min mat)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> minimum over all <code class="language-plaintext highlighter-rouge">matrix</code> argument’s entries.</li>
</ul>
<h4 id="maxima">Maxima</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-rowmax mat)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of row maxima of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-colmax mat)</code> - Returns row <code class="language-plaintext highlighter-rouge">matrix</code> of column maxima of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-max mat)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> maximum over all <code class="language-plaintext highlighter-rouge">matrix</code> argument’s entries.</li>
</ul>
<h4 id="midrange">Midrange</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-rowmidr mat)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of row midranges of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-colmidr mat)</code> - Returns row <code class="language-plaintext highlighter-rouge">matrix</code> of column midranges of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-midr mat)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> midrange over all <code class="language-plaintext highlighter-rouge">matrix</code> argument’s entries.</li>
</ul>
<h4 id="generalized-means">Generalized Means</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-rowmean mat typ)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of row power-means of argument <code class="language-plaintext highlighter-rouge">matrix</code> of power <code class="language-plaintext highlighter-rouge">symbol</code> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-colmean mat typ)</code> - Returns row <code class="language-plaintext highlighter-rouge">matrix</code> of column power-means of argument <code class="language-plaintext highlighter-rouge">matrix</code> of power <code class="language-plaintext highlighter-rouge">symbol</code> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-mean mat typ)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> power-mean over all <code class="language-plaintext highlighter-rouge">matrix</code> argument’s entries of power <code class="language-plaintext highlighter-rouge">symbol</code> argument.</li>
</ul>
<h4 id="vector-and-matrix-norms">Vector and Matrix Norms</h4>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-rownorm mat typ)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of row norms of argument <code class="language-plaintext highlighter-rouge">matrix</code> of type <code class="language-plaintext highlighter-rouge">symbol</code> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-colnorm mat typ)</code> - Returns row <code class="language-plaintext highlighter-rouge">matrix</code> of column norms of argument <code class="language-plaintext highlighter-rouge">matrix</code> of type <code class="language-plaintext highlighter-rouge">symbol</code> argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-norm mat typ)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> matrix norm over all <code class="language-plaintext highlighter-rouge">matrix</code> argument’s entries of type <code class="language-plaintext highlighter-rouge">symbol</code> argument.</li>
</ul>
<p>Overall, these functions make up the basic matrix transformation operations,
which in <code class="language-plaintext highlighter-rouge">matrico</code> are specializations of the generic <em>map</em> and <em>reduce</em> matrix functions.
In the next post, I will go through the core linear algebra and analysis functions of <code class="language-plaintext highlighter-rouge">matrico</code>.</p>Christian Himpematrico’s matrico Module (Part III)Matrico Module II2022-09-30T00:00:00+00:002022-09-30T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2022/09/30/matrico-matrico-ii<h2 id="matricos-matrico-module-part-ii"><code class="language-plaintext highlighter-rouge">matrico</code>’s <code class="language-plaintext highlighter-rouge">matrico</code> Module (Part II)</h2>
<p><strong>Series</strong>:</p>
<ul>
<li><a href="http://numerical-schemer.xyz/2022/09/16/matrico-matrico-i.html">Part I</a></li>
<li>Part II</li>
<li><a href="http://numerical-schemer.xyz/2022/10/17/matrico-matrico-iii.html">Part III</a></li>
<li><a href="http://numerical-schemer.xyz/2022/11/30/matrico-matrico-iiii.html">Part IIII</a></li>
<li><a href="http://numerical-schemer.xyz/2022/12/15/matrico-matrico-v.html">Part V</a></li>
</ul>
<p>In this second part of the series of posts describing the <code class="language-plaintext highlighter-rouge">matrico</code> module, I will focus on the
<em>Constructor</em>, <em>Dimension</em>, <em>Predicate</em>, and <em>Accessor</em> type functions, which are contained in
<a href="https://github.com/gramian/matrico/blob/main/src/mx.scm"><code class="language-plaintext highlighter-rouge">src/mx.scm</code></a> Scheme source code file,
that is <strong>included</strong> into the module’s main file <code class="language-plaintext highlighter-rouge">matrico.scm</code>.</p>
<h3 id="constructors">Constructors</h3>
<p>As I mentioned in an <a href="http://numerical-schemer.xyz/2022/08/04/matrico-matrix.html">earlier post</a>,
the actual <code class="language-plaintext highlighter-rouge">matrix</code> record is hidden from the user. Hence, to instantiate a <code class="language-plaintext highlighter-rouge">matrix</code>, constructors are needed,
which are provided in terms of basic as well as specialized matrices and column matrices;
but also a way to manually enter a matrix as a list of lists is given:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx rows cols val)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> of row and column argument <code class="language-plaintext highlighter-rouge">number</code>s and all entries set to <code class="language-plaintext highlighter-rouge">flonum</code> value.</li>
<li><code class="language-plaintext highlighter-rouge">(mx% lst)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> from row-major <code class="language-plaintext highlighter-rouge">list</code>-of-<code class="language-plaintext highlighter-rouge">list</code>-of-<code class="language-plaintext highlighter-rouge">flonum</code>s.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-identity dims)</code> - Returns an <a href="https://en.wikipedia.org/wiki/Identity_matrix">identity</a> <code class="language-plaintext highlighter-rouge">matrix</code> of dimension argument <code class="language-plaintext highlighter-rouge">number</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-exchange dims)</code> - Returns an <a href="https://en.wikipedia.org/wiki/Exchange_matrix">exchange</a> <code class="language-plaintext highlighter-rouge">matrix</code> of dimension argument <code class="language-plaintext highlighter-rouge">number</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-hilbert dims)</code> - Returns a <a href="https://en.wikipedia.org/wiki/Hilbert_matrix">Hilbert</a> <code class="language-plaintext highlighter-rouge">matrix</code> of dimension argument <code class="language-plaintext highlighter-rouge">number</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-pascal dims)</code> - Returns a <a href="https://en.wikipedia.org/wiki/Pascal_matrix">lower triangular Pascal</a> <code class="language-plaintext highlighter-rouge">matrix</code> of dimension argument <code class="language-plaintext highlighter-rouge">number</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-lehmer rows cols)</code> - Returns a <a href="https://en.wikipedia.org/wiki/Lehmer_matrix">Lehmer</a> <code class="language-plaintext highlighter-rouge">matrix</code> of row and column dimension arguments <code class="language-plaintext highlighter-rouge">number</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-random rows cols low upp)</code> - Returns uniformly random <code class="language-plaintext highlighter-rouge">matrix</code> within <code class="language-plaintext highlighter-rouge">flonum</code> lower and upper limit arguments of row and column argument <code class="language-plaintext highlighter-rouge">number</code>s.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-tridiag dims low mid upp)</code> - Returns tridiagonal <code class="language-plaintext highlighter-rouge">matrix</code> with lower, middle, and upper band value arguments of dimension argument <code class="language-plaintext highlighter-rouge">number</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-unit dims num)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of dimension argument <code class="language-plaintext highlighter-rouge">number</code> with all entries zero except at index argument <code class="language-plaintext highlighter-rouge">number</code> where it is one.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-iota dims)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> of dimension argument <code class="language-plaintext highlighter-rouge">number</code>, with entries corresponding to the row index.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-linspace x y num)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> with linearly interpolated rows between column <code class="language-plaintext highlighter-rouge">matrix</code> arguments and <code class="language-plaintext highlighter-rouge">number</code> of columns argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-logspace x y num)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> with base-10 logarithmically interpolated rows between column <code class="language-plaintext highlighter-rouge">matrix</code> arguments and <code class="language-plaintext highlighter-rouge">number</code> of columns argument.</li>
</ul>
<h3 id="dimensions">Dimensions</h3>
<p>Next, functions are provided to determine the dimensionality of a <code class="language-plaintext highlighter-rouge">matrix</code>:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-cols mat)</code> - Returns <code class="language-plaintext highlighter-rouge">number</code> of columns of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-rows mat)</code> - Returns <code class="language-plaintext highlighter-rouge">number</code> of rows of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-numel mat)</code> - Returns <code class="language-plaintext highlighter-rouge">number</code> of entries of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-dims mat)</code> - Returns <code class="language-plaintext highlighter-rouge">number</code> of dimensions of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
</ul>
<h3 id="predicates">Predicates</h3>
<p>Also, the properties of one <code class="language-plaintext highlighter-rouge">matrix</code> or jointly of two <code class="language-plaintext highlighter-rouge">matrix</code>es, need to be testable.
To this end a set of predicates is implemented, including a <code class="language-plaintext highlighter-rouge">flonum</code>-safe equality comparison of <code class="language-plaintext highlighter-rouge">matrix</code>es:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx? any)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument is a <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-col? mat)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code> has just one column.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-row? mat)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code> has just one row.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-scalar? mat)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code> has just one column and row.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-vector? mat)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code> has just one column or row.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-square? mat)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code> has same number of rows and columns.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-samecols? x y)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code>es have same number of columns.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-samerows? x y)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code>es have same number of rows.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-samedims? x y)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if argument <code class="language-plaintext highlighter-rouge">matrix</code>es have same number of rows and columns.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-any? pred mat)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if at least one entry of the argument <code class="language-plaintext highlighter-rouge">matrix</code> fulfills predicate argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-all? pred mat)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if all entries of argument <code class="language-plaintext highlighter-rouge">matrix</code> fulfill predicate argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx=? x y tol)</code> - Returns <code class="language-plaintext highlighter-rouge">boolean</code> answering if the entry-wise absolute difference between argument <code class="language-plaintext highlighter-rouge">matrix</code>es is below <code class="language-plaintext highlighter-rouge">flonum</code> tolerance argument.</li>
</ul>
<h3 id="accessors">Accessors</h3>
<p>Given functions to create, measure and test matrices, a basic functionality completing this set of basic matrix functions,
are functions to get or set entries or parts of a <code class="language-plaintext highlighter-rouge">matrix</code>:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(mx-ref11 mat)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> being the entry in the first column and first row of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-ref mat row col)</code> - Returns <code class="language-plaintext highlighter-rouge">flonum</code> being the entry at column and row arguments of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-set mat row col val)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> with entry at column and row arguments of argument <code class="language-plaintext highlighter-rouge">matrix</code> set to <code class="language-plaintext highlighter-rouge">flonum</code> value argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-set! mat row col val)</code> - Returns void, mutates entry at column and row arguments of argument <code class="language-plaintext highlighter-rouge">matrix</code> to <code class="language-plaintext highlighter-rouge">flonum</code> value argument.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-col mat col)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being the column at index argument of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-row mat row)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being the row at index argument of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-diag mat)</code> - Returns column <code class="language-plaintext highlighter-rouge">matrix</code> being the main diagonal of argument <code class="language-plaintext highlighter-rouge">matrix</code>.</li>
<li><code class="language-plaintext highlighter-rouge">(mx-submatrix mat row1 row2 col1 col2)</code> - Returns <code class="language-plaintext highlighter-rouge">matrix</code> being the sub-matrix between row and column index arguments of the <code class="language-plaintext highlighter-rouge">matrix</code> argument.</li>
</ul>
<p>In the next part, I will describe the basic arithmetic and calculator functions
in <code class="language-plaintext highlighter-rouge">matrico</code> via <em>Expanders</em>, <em>Mappers</em>, and <em>Reducers</em>.</p>Christian Himpematrico’s matrico Module (Part II)Matrico Module I2022-09-16T00:00:00+00:002022-09-16T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2022/09/16/matrico-matrico-i<h2 id="matricos-matrico-module-part-i"><code class="language-plaintext highlighter-rouge">matrico</code>’s <code class="language-plaintext highlighter-rouge">matrico</code> Module (Part I)</h2>
<p><strong>Series</strong>:</p>
<ul>
<li>Part I</li>
<li><a href="http://numerical-schemer.xyz/2022/09/30/matrico-matrico-ii.html">Part II</a></li>
<li><a href="http://numerical-schemer.xyz/2022/10/17/matrico-matrico-iii.html">Part III</a></li>
<li><a href="http://numerical-schemer.xyz/2022/11/30/matrico-matrico-iiii.html">Part IIII</a></li>
<li><a href="http://numerical-schemer.xyz/2022/12/15/matrico-matrico-v.html">Part V</a></li>
</ul>
<p>The <code class="language-plaintext highlighter-rouge">matrico</code> module is the user-facing part of the <code class="language-plaintext highlighter-rouge">matrico</code> project.
Over the next few posts, I will describe the available functions therein.
Almost all functions in this module are prefixed with “<code class="language-plaintext highlighter-rouge">mx</code>”,
with the exception of the <code class="language-plaintext highlighter-rouge">matrico</code> function, implemented in the <a href="https://github.com/gramian/matrico/blob/main/matrico.scm"><code class="language-plaintext highlighter-rouge">matrico.scm</code></a> source file.
While the functions prefixed with “<code class="language-plaintext highlighter-rouge">mx</code>” are operating on, or returning, matrices,
the <code class="language-plaintext highlighter-rouge">matrico</code> function is a metadata function providing information about <code class="language-plaintext highlighter-rouge">matrico</code> itself (or the current machine).
I start here by describing this “<code class="language-plaintext highlighter-rouge">matrico</code>“-function:</p>
<p>On the one hand, the <code class="language-plaintext highlighter-rouge">matrico</code> function is a <a href="https://en.wikipedia.org/wiki/Thunk#Functional_programming">thunk</a>
(zero argument function) that explains its own use, so knowing this one function allows a REPL user to explore
<code class="language-plaintext highlighter-rouge">matrico</code> in a self-contained manner:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico)</code> - returns void, prints a short help on the use of the <code class="language-plaintext highlighter-rouge">matrico</code> function with an symbol argument.</li>
</ul>
<p>On the other hand, the <code class="language-plaintext highlighter-rouge">matrico</code> function is also a one-argument function taking a symbol argument.
Below, the specific symbols recognized are listed.
First, functionality to list all of <code class="language-plaintext highlighter-rouge">matrico</code>’s “<code class="language-plaintext highlighter-rouge">mx</code>“-prefixed functions is given:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico 'list)</code> - returns void, prints list of all matrix processing functions.</li>
</ul>
<p>Next, a way to print a minimal description of <code class="language-plaintext highlighter-rouge">matrico</code> and a link to the user-facing documentation is provided:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico 'about)</code> - returns void, prints summary of <code class="language-plaintext highlighter-rouge">matrico</code>.</li>
</ul>
<p>Also, printing the <code class="language-plaintext highlighter-rouge">matrico</code> logo, used on-load of the module (egg) is encapsuled:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico 'banner)</code> - returns void, prints the <code class="language-plaintext highlighter-rouge">matrico</code> logo, name, and version.</li>
</ul>
<p>The <code class="language-plaintext highlighter-rouge">matrico</code> version can also be returned by the <code class="language-plaintext highlighter-rouge">matrico</code> function:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico 'version)</code> - returns pair of integers representing major and minor version semantic number in the head (<code class="language-plaintext highlighter-rouge">car</code>) and tail (<code class="language-plaintext highlighter-rouge">cdr</code>) respectively.</li>
</ul>
<p>A pair of integer numbers is returned instead of a string, typically used in CHICKEN Scheme for versions,
so they can directly be compared.</p>
<p>Next, is a symbol argument that causes returning information on how to cite this module.
I included this due to my experiences in research software development,
so in the (unlikely) case somebody uses <code class="language-plaintext highlighter-rouge">matrico</code> for science, it is clear how to cite it:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico 'citation)</code> - returns void, prints preferred citation string for <code class="language-plaintext highlighter-rouge">matrico</code>.</li>
</ul>
<p>Lastly, I was contemplating how to classify the hardware <code class="language-plaintext highlighter-rouge">matrico</code> is running on.
Initially, I looked into the included <a href="http://wiki.call-cc.org/man/5/Module%20(chicken%20platform)">(chicken platform)</a> module,
which did not provide an adequate function, as I am interested in a (rough) performance assessment.
Another option is to read out a processor identifier,
yet this has to be adapted for every operating systems, i.e. Linux (<code class="language-plaintext highlighter-rouge">lscpu</code>), MacOS (<code class="language-plaintext highlighter-rouge">sysctl</code>), Windows (<code class="language-plaintext highlighter-rouge">wmic</code>),
and even if one would be willing to maintain this, a performance estimate has to be deduced by looking up capabilities
in the chip’s (online) documentation.
The Linux kernel presented a workable alternative: It tests the processor’s speed on boot by looping through an empty loop
for a high number of times (so called <code class="language-plaintext highlighter-rouge">busy wait</code>) and measuring the elapsed time.
This measure is called <a href="https://en.wikipedia.org/wiki/BogoMips">BogoMips</a>,
for “Bogus” million-instructions-per-second, and also implemented in <code class="language-plaintext highlighter-rouge">matrico</code>:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico 'benchmark)</code> - returns number of busy-wait million-instructions-per-second.</li>
</ul>
<p>I note, as have other commentators, that this “benchmark” is rather crude, nonetheless,
and particularly since CHICKEN Scheme is single-threaded, this function gives a hint on the processor’s throughput,
but does not rank performance, as the lead in BogoMips of the “Raspberry Pi 4” over the “M2” illustrates.</p>
<p>Last but not least, any other symbol is tested if it is a name of a matrix function of <code class="language-plaintext highlighter-rouge">matrico</code>.
This means, true is returned, if the symbol is the name of a matrix function in <code class="language-plaintext highlighter-rouge">matrico</code>;
at the very least, the symbol needs to start with “<code class="language-plaintext highlighter-rouge">mx</code>”.
However, the purpose of this function is given by its side-effect,
which is printing the docstring of the “<code class="language-plaintext highlighter-rouge">mx</code>“-function to the terminal,
if the symbol is the name of such function, and thus supply an online help for the REPL:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(matrico 'sym)</code> - returns boolean, answering if argument symbol is a name of an “<code class="language-plaintext highlighter-rouge">mx</code>“-function in the <code class="language-plaintext highlighter-rouge">matrico</code> module and if, print its docstring.</li>
</ul>
<p>Together with this functionality, a user just needs to know the <code class="language-plaintext highlighter-rouge">(matrico)</code> thunk to explore all of <code class="language-plaintext highlighter-rouge">matrico</code>:</p>
<ol>
<li>Help about the single argument <code class="language-plaintext highlighter-rouge">matrico</code> function via <code class="language-plaintext highlighter-rouge">(matrico)</code>.</li>
<li>List all of <code class="language-plaintext highlighter-rouge">matrico</code>’s matrix functions via <code class="language-plaintext highlighter-rouge">(matrico 'list)</code>.</li>
<li>Print help on a matrix function via <code class="language-plaintext highlighter-rouge">(matrico 'mx-...)</code>.</li>
</ol>
<p>The associated source file <code class="language-plaintext highlighter-rouge">matrico.scm</code> is <a href="https://github.com/gramian/matrico/blob/main/matrico.scm">here</a>, which defines the module.
The remaining user-facing “<code class="language-plaintext highlighter-rouge">mx</code>“-functions are contained in the source file <code class="language-plaintext highlighter-rouge">src/mx.scm</code>,
which is included into the <code class="language-plaintext highlighter-rouge">matrico.scm</code> source file and will be detailed in the subsequent posts.</p>Christian Himpematrico’s matrico Module (Part I)CHICKEN plist Module2022-08-26T00:00:00+00:002022-08-26T00:00:00+00:00https://gramian.github.io/numerical-schemer.xyz/2022/08/26/chicken-plist<h2 id="chicken-schemes-included-plist-module">CHICKEN Scheme’s included <code class="language-plaintext highlighter-rouge">plist</code> module</h2>
<p>Symbol property lists are a long-standing Lisp feature
(see <a href="http://jmc.stanford.edu/articles/lisp/lisp.pdf#page=10">History of LISP</a>)
which attaches a empty list to each (exisiting or defined) symbol.
This list is similar to an associative array, in that data can be stored in conjunction with a naming property;
Clojure’s <a href="https://clojure.org/reference/metadata">metadata</a> functionality is related in concept.
Practically, this allows to attach named (meta-)data to unique identifiers.
In <code class="language-plaintext highlighter-rouge">CHICKEN</code>, property lists are provided via the included <a href="http://wiki.call-cc.org/man/5/Module%20(chicken%20plist)"><code class="language-plaintext highlighter-rouge">(chicken plist)</code></a> module.
Internally, these property lists are realized, not as a list of pairs,
but a list alternating between property and value for all property-value pairs.
The two central functions from this module are:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">(put! sym prop val)</code> - Returns value argument, which is added a under property argument to the property list of argument symbol.</li>
<li><code class="language-plaintext highlighter-rouge">(get sym prop)</code> - Returns value of property argument in property list of symbol argument, or false if symbol or property does not exist.</li>
</ul>
<p>In <code class="language-plaintext highlighter-rouge">matrico</code>, property lists are used to emulate <a href="https://en.wikipedia.org/wiki/Docstring">Docstring</a>s.
This is achieved by using the <code class="language-plaintext highlighter-rouge">define*</code> macro from the <a href="http://numerical-schemer.xyz/2022/06/12/matrico-utils.html"><code class="language-plaintext highlighter-rouge">utils</code></a> module,
which assigns a string to a symbol (<code class="language-plaintext highlighter-rouge">put!</code>) with the name of the binding used in the associated <code class="language-plaintext highlighter-rouge">define</code>.
Then, a <code class="language-plaintext highlighter-rouge">matrico</code> function is provided to retrieve the docstring (<code class="language-plaintext highlighter-rouge">get</code>) for a given function name passed as a symbol and display it.</p>
<p>Next, in a multi-part series, the user-facing functions of <code class="language-plaintext highlighter-rouge">matrico</code> are detailed.</p>Christian HimpeCHICKEN Scheme’s included plist module