\documentclass[a4paper]{article}

\addtolength{\oddsidemargin}{-10pt}
\addtolength{\textwidth}{50pt}
\addtolength{\textheight}{70pt}
\addtolength{\voffset}{-40pt}

\title{Mobile antenna model description}
\author{Jan Šimbera}
\date{\today}

\usepackage{amsmath}
\usepackage{amssymb}
\usepackage[pdftex,unicode,hidelinks]{hyperref}
\usepackage[pdftex]{graphicx}

\begin{document}
\maketitle
\begin{abstract}
This document outlines a model that describes the spread of cell phone signal
for the purposes of mapping cell phone antenna coverage areas and detecting
the user's location somewhat more accurately.
\end{abstract}

\section{Model description} \label{sec:antmodel}

\subsection{Assumptions}
The connection of the cell phone to the mobile network is facilitated through
a system of antennas. Usually, the operator shares very little information
about these antennas; nevertheless, some parameters, such as antenna location,
are almost always available, sometimes through crowdsourced registers. We thus
assume the antenna locations $\vec{x_0} = (x_0,y_0)$ are fixed and known; we
also assume a planar coordinate system for simplicity.

\subsection{Signal spread model}\label{sec:sigmodel}
We assume three orthogonal effects on the signal strength $S_i(\vec{x})$
of antenna $i$ for a given location $\vec{x} = (x,y)$, that is:
\begin{itemize}
\item antenna power $P_i$,
\item distance from the antenna $d_i(\vec{x}) = ||\vec{x} - \vec{x_i}||$,
\item azimuth of the vector from the antenna to the location
    $\varphi_i(\vec{x}) = \arctan{\frac{y - y_i}{x - x_i}}$.
\end{itemize}

We can employ a probabilistic approach to the problem: we first model the
probability of a user being at a given location $\vec{x}$ if we know that he
or she is connected to an antenna $i$ and then use the Bayesian rule to
determine signal strength as the inverse conditional probability:
\begin{equation}
S_i(\vec{x}) = P(i|\vec{x}) = P(\vec{x}|i) \frac{P(i)}{P(\vec{x})}
\end{equation}
We can isolate the $P(i)$ as the antenna power $P_i$ and amalgamate the
remaining two terms to make the model agnostic to an apriori distribution of
users (i.e. population/settlement density):
\begin{equation}
S_i(\vec{x}) = P_i \frac{P(\vec{x}|i)}{P(\vec{x})} = P_i \cdot L_i(\vec{x})
\end{equation}
The resulting term $L_i(\vec{x})$ should capture the remaining two effects of
distance and azimuth. As we assume the effects to be orthogonal, we can
express it as
\begin{equation}
L_i(\vec{x}) = f_d(d_i(\vec{x})) \cdot f_a(\varphi_i(\vec{x}))
\end{equation}

\subsubsection{Distance decay}
As we operate on macroscopic scales, the distance decay of an EM signal is
governed by an inverse square law:
\begin{equation}
S_i(\vec{x}) \propto d_i(\vec{x})^{-2}
\end{equation}
This, however, would imply the signal strength goes to infinity near the
location of the antenna. Therefore, it is useful to employ a small correcting
factor. Using this, we arrive at a probabilistic description by a Cauchy
distribution, which is useful:
\begin{equation}
d_i(\vec{x}) \sim \mathrm{Cauchy}(0,\gamma_i)
\end{equation}
where $\gamma_i$ is the distance decay scale factor. We can use the probability
density function of the Cauchy distribution as our distance decay function:
\begin{equation}
f_d(d_i(\vec{x})) = \frac{1}{
    \pi \gamma_i \left[1 + \left( \frac{d_i(\vec{x})}{\gamma_i} \right)^2 \right]
} = \frac{\gamma_i}{\pi [d_i(\vec{x})^2 + \gamma_i^2]}
\end{equation}

\subsubsection{Angular distribution}
To model the angular dispersion of the signal, we can use the von Mises
distribution in a manner similar to the Cauchy distribution for distance decay.
The von Mises distribution is a circular analogue of the normal distribution:
\begin{equation}
f(\varphi) = \frac{\exp{[\kappa \cos{(\varphi - \alpha)}]}}{2\pi I_0(\kappa)}
\end{equation}
where $I_0(\kappa)$ is the modified Bessel function of order zero, $\alpha$ is
the principal angle and $\kappa$ is the directional concentration.

Using this, we define
\begin{equation}
f_a(\varphi_i(\vec{x})) = \frac
    {\exp{[\kappa_i \cos{(\varphi_i(\vec{x}) - \alpha_i)}]}}
    {2\pi I_0(\kappa_i)}
\end{equation}
which leaves us with two parameters for the antenna: orientation (principal
angle) $\alpha$ and angular signal concentration (narrowness) $\kappa$.

\subsubsection{Final signal strength equation}
Combining the above results, we arrive at the final signal model result
\begin{align}
S_i(\vec{x}) &= P_i \cdot f_d(d_i(\vec{x})) \cdot f_a(\varphi_i(\vec{x}))\\
&= P_i \frac
    {\gamma_i \exp{[\kappa_i \cos{(\varphi_i(\vec{x}) - \alpha_i)}]}}
    {2\pi^2 I_0(\kappa_i) [d_i(\vec{x})^2 + \gamma_i^2]}\\
&= \frac{P_i \gamma_i}{2\pi^2 I_0(\kappa_i)} \cdot \frac
    {\exp{[\kappa_i \cos{(\varphi_i(\vec{x}) - \alpha_i)}]}}
    {d_i(\vec{x})^2 + \gamma_i^2}\\
&= K_i \cdot \frac
    {\exp{[\kappa_i \cos{(\varphi_i(\vec{x}) - \alpha_i)}]}}
    {d_i(\vec{x})^2 + \gamma_i^2}
\end{align}

Since the signal strengths are relative to each other, we should specify a
condition on their absolute values; this can be formulated e.g. as
\begin{equation} \label{eq:powersumcond}
\sum_{a=1}^{n_a} P_a \gamma_a = 1
\end{equation}

\subsection{Antenna parameters}
The signal strength model uses four additional parameters (in addition to
location) to describe the antennas:
\begin{itemize}
\item Antenna orientation azimuth $\alpha \in [0;2\pi)$ (with zero pointing
    north).
\item Antenna power or overall signal strength $P \in \mathbb{R}^{+}$. This
    is somehow related to the power of the antenna transmitter.
\item Distance decay scale parameter $\gamma \in \mathbb{R}^{+}$, defining
    the rate of signal strength diminishing with increasing distance from the
    antenna. This can be related e.g. to the vertical orientation of the
    antenna with respect to the surface.
\item Angular concentration of antenna signal $\kappa \in \mathbb{R}^{+}_{0}$.
    The higher the concentration (narrowness), the more the signal power is
    concentrated along the direction of the antenna orientation. $\kappa = 0$
    means the antenna is isotropic and radiates the signal uniformly in all
    directions.
\end{itemize}
Sometimes, some of the parameters may be known a priori, such as the antenna
azimuths. The rest of the parameters need to be estimated from the antenna
connection data, which should be the next large task.

\section{Model parameter estimation}
To estimate the antenna parameters, several variants of data sources may be
used. In this estimation scenario, we consider a set of $n_p$ places $p$ with
locations $\vec{x}_p$. We denote the signal strength of a given antenna
$a$ at that place as $S_{ap}$, the distance $d_{ap}$ and angle $\varphi_{ap}$
of a given place from it as in \ref{sec:sigmodel}.

In addition to the place data, we enter data about a set of $n_u$ users and
their simultaneous connections to antennas (that is, how often they are
connected to what antennas while not moving themselves).
Therefore, we propose using night-time signalling (SS.7 logs) or CDR
data where antenna connection variability is most likely to come from antenna
signal strength variation, not from user movements. Given this, we may derive
for each user a set of connection time fractions $f_{ua}$ denoting the
likelihood of the connection of the user to the respective antennas, satisfying
$\sum_{a=1}^{n_a} f_{ua} = 1 \,\forall u$.

\subsection{Estimation with known locations} \label{sec:locest}
If we have an additional data source that allows us to determine the actual
night-time positions of the users (such as independent mobility tracker app
logs), the model estimation becomes simpler because we can compute the user
location probabilities with respect to the places $p$ directly as $p_{up}$.

The algorithm works as an EM procedure, trying to estimate the signal strengths
for all places and antennas with antenna parameters as proxy variables.
The E step estimates the antenna parameters from signal strengths using OLS,
with the initial values obtained from location averaging, and the M step
estimates the signal strengths from the antenna parameters using the equations
from \ref{sec:antmodel}.

\subsubsection{Antenna dominance fractions}
Dominance fractions denote how much the given place is dominated by a signal
from a given antenna, an therefore, the probability of a user at a given
location being connected to the antenna:
\begin{equation}
\Psi_{ap} = \frac
    {\sum_{u=1}^{n_u} p_{up} f_{ua}}
    {\sum_{u=1}^{n_u} p_{up}}
\end{equation}
This ensures that $\sum_{a=1}^{n_a} \Psi_{ap} = 1 \,\forall p$.

\subsubsection{Expectation step}
In this step, we compute the optimal antenna parameters using OLS on a
linearized signal strength function (with parameters from the previous round)
set as equal to the E-computed signal strength:
\begin{align}
\frac{1}{P_a^{(i)}} &\cdot P_a^{(i+1)} \nonumber \\
+ \frac
    {d_{ap}^2 - {\gamma_a^{(i)}}^2}
    {\gamma_a^{(i)} (d_{ap}^2 + {\gamma_a^{(i)}}^2)}
    &\cdot \gamma_a^{(i+1)} \nonumber \\
+ \kappa_a^{(i)} \sin{(\varphi_{ap} - \alpha_a^{(i)})}
    &\cdot \alpha_a^{(i+1)} \nonumber \\
+ \left[
        \cos{(\varphi_{ap} - \alpha_a^{(i)})}
        - \frac{I_1(\kappa_a^{(i)})}{I_0(\kappa_a^{(i)})}
    \right] &\cdot \kappa_a^{(i+1)} =\\
\frac{S_{ap}^{(i+1)}}{S_{ap}^{(i)}}
+ 1
+ \frac
    {d_{ap}^2 - {\gamma_a^{(i)}}^2}
    {d_{ap}^2 + {\gamma_a^{(i)}}^2}
+ \kappa_a^{(i)} \alpha_a^{(i)} \sin{(\varphi_{ap} - \alpha_a^{(i)})}
&+ \kappa_a^{(i)} \left[
        \cos{(\varphi_{ap} - \alpha_a^{(i)})}
        - \frac{I_1(\kappa_a^{(i)})}{I_0(\kappa_a^{(i)})}
    \right] \nonumber
\end{align}

\subsubsection{Maximization step}
In this step, we estimate the signal strengths for places from their antenna
dominances and antenna parameters:
\begin{equation}
S_{ap}^{(i+1)} = \Psi_{ap} \sum_{b=1}^{n_a} \frac
    {P_b^{(i)} \gamma_b^{(i)} \exp{[
        \kappa_b^{(i)} \cos{(\varphi_{bp} - \alpha_b^{(i)})}
    ]}}
    {2\pi^2 I_0(\kappa_b^{(i)}) [d_{bp}^2 + {\gamma_b^{(i)}}^2]}\\
\end{equation}

\subsubsection{Initial antenna parameter estimations}
The initial values can be obtained as follows:

\paragraph{Antenna principal angles} $\alpha_a$ -- if not known -- can be
determined as weighted circular means
\begin{equation}
\alpha_a = \arctan \frac
    {\sum_{p=1}^{n_p} \Psi_{ap} \sin \varphi_{ap}}
    {\sum_{p=1}^{n_p} \Psi_{ap} \cos \varphi_{ap}}
\end{equation}

\paragraph{Antenna angular concentrations} $\kappa_a$ can be determined
using the von Mises-Fisher iterative estimation procedure from the parameter
$\bar{R}_a$:
\begin{equation}
\bar{R}_a = \frac
    {\left(\sum_{p=1}^{n_p} \Psi_{ap} \sin \varphi_{ap}\right)^2
        + \left(\sum_{p=1}^{n_p} \Psi_{ap} \cos \varphi_{ap}\right)^2}
    {\left(\sum_{p=1}^{n_p} \Psi_{ap}\right)^2}
\end{equation}
With this parameter, we can produce the initial estimate for $\kappa_a$ as
\begin{equation}
\kappa_a^{(0)} = \frac
    {\bar{R}_a (2 - \bar{R}_a^2)}
    {1 - \bar{R}_a^2}
\end{equation}
and then repeat the following equation until convergence
\begin{equation}
\kappa_a^{(i+1)} = \kappa_a^{(i)} - \frac
    {A_p(\kappa_a^{(i)}) - \bar{R}_a}
    {1 - A_p(\kappa_a^{(i)})^2 - \frac{A_p(\kappa_a^{(i)})}{\kappa_a^{(i)}}}
\end{equation}
where $A_p(x) = \frac{I_1(x)}{I_0(x)}$ is the ratio of modified Bessel functions
of the first and zeroth order respectively. Three iterations are usually
sufficient.

\paragraph{Antenna distance decay parameter} $\gamma_a$ may be estimated as
the weighted mean distance of a dominated place from the antenna:
\begin{equation}
\gamma_a = \frac
    {\sum_{p=1}^{n_p} \Psi_{ap} d_{ap}}
    {\sum_{p=1}^{n_p} \Psi_{ap}}
\end{equation}

\paragraph{Strengths of the antennas} $P_a$, to honor the condition from
\eqref{eq:powersumcond}, can be estimated as the relative size of the antenna's
area of dominance to the total area of study. We need to factor out the
general influence of the distance decay parameter, too:
\begin{equation}
P_a = \frac
    {\sum_{p=1}^{n_p} \Psi_{ap} A_p}
    {\gamma_a \sum_{p=1}^{n_p} A_p}
\end{equation}
where $A_p$ is the area corresponding to place $p$ -- if the places are only
given as points, the areas may be computed e. g. using Voronoi polygons.

\subsection{Estimation with unknown locations}
If we do not have data about real user locations $p_{up}$, we can supply them by
employing an EM estimation procedure over the proposed approach.

To do this, it is useful to know at least roughly the expected user densities
over the places used $w_p$. These densities may be derived from an apriori
population distribution measure such as a census grid or, alternatively, a land
use layer such as GHSL aggregated to a reasonable level.

\subsubsection{Expectation step}
This step of the procedure estimates the user locations
from signal strengths in those places by computing place affinities as
\begin{equation}
a_{up}^{(i+1)} = \left(
    1 + \frac
        {w_p - \sum_{u=1}^{n_u} p_{up}^{(i)}}
        {w_p + \sum_{u=1}^{n_u} p_{up}^{(i)}}
\right)^{\frac{1}{k_c}}
\sqrt{
    \frac{1}{n_a} \sum_{a=1}^{n_a} \left(
        f_{ua} - \frac{S_{ap}^{(i)}}{\sum_{b=1}^{n_a} S_{bp}^{(i)}}
    \right)^2
}
\end{equation}
where $k_c$ is the crowding tolerance coefficient specifying the degree of
adherence to the apriori place weights $w_p$. Its higher values mean a more
different spatial distribution of users can arise.

Then the actual localization probabilities are produced by normalizing the
affinities to sum to one:
\begin{equation}
p_{up}^{(i+1)} = \frac
    {a_{up}^{(i+1)}}
    {\sum_{q=1}^{n_p} a_{uq}^{(i+1)}}
\end{equation}

\subsubsection{Maximization step}
This step of the procedure uses the method from
\ref{sec:locest} to derive $S_{ap}^{(i)}$ from $p_{up}^{(i)}$.
\end{document}


\documentclass[12pt]{cdblatex}
\usepackage{examples}
\usepackage{fancyhdr}
\usepackage{footer}

\begin{document}

% ============================================================================================
\bgroup
\CdbSetup{action=hide}
\begin{cadabra}
   import cdblib
   checkpoint_file = 'tests/semantic/output/example-13.json'
   cdblib.create (checkpoint_file)
   checkpoint = []
\end{cadabra}
\egroup

\clearpage

% =============================================================================
\section*{Example 13a The Weyl tensor vanishes in 3d -- direct proof}

\begin{cadabra}
   {x,y,z}::Coordinate.
   {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,u,v,w#}::Indices (values={x,y,z},position=independent).

   \partial{#}::PartialDerivative.

   g_{a b}::Metric.
   g^{a b}::InverseMetric.

   {\partial_{a b}{g_{c d}},\partial_{a}{g_{b c}},g_{a b},g^{a b}}::SortOrder.

   GammaU := \Gamma^{a}_{b c} ->  1/2 g^{a d} (   \partial_{b}{g_{d c}}
                                                + \partial_{c}{g_{b d}}
                                                - \partial_{d}{g_{b c}}). # cdb(Gamma.000,GammaU)

   GammaD := \Gamma_{a b c} ->  1/2 (   \partial_{b}{g_{a c}}
                                      + \partial_{c}{g_{b a}}
                                      - \partial_{a}{g_{b c}}).           # cdb(Gamma.010,GammaD)

   Rabcd := R_{a b c d} ->   \partial_{c}{\Gamma_{a b d}}
                           - \partial_{d}{\Gamma_{a b c}}
                           + \Gamma_{e a d} \Gamma^{e}_{b c}
                           - \Gamma_{e a c} \Gamma^{e}_{b d}.             # cdb (Rabcd.000,Rabcd)

   Rab     := R_{a b} -> g^{c d} R_{a c b d}.                             # cdb (Rab.000,Rab)

   Rscalar := R -> g^{a b} R_{a b}.                                       # cdb (R.000,Rscalar)

   # Weyl in 3-dimensions

   Cabcd := R_{a b c d} - (R_{a c} g_{b d} - R_{a d} g_{b c})
                        - (g_{a c} R_{b d} - g_{a d} R_{b c})
                  + 1/2 R (g_{a c} g_{b d} - g_{a d} g_{b c}).            # cdb (ex-13a.100,Cabcd)

   # Use 8 Cabcd to clear the fractions

   EightCabcd := 8 @(Cabcd).                                              # cdb (ex-13a.110,EightCabcd)

   substitute     (Cabcd,Rscalar)
   substitute     (Cabcd,Rab)
   substitute     (Cabcd,Rabcd)
   substitute     (Cabcd,GammaU)
   substitute     (Cabcd,GammaD)

   distribute     (Cabcd)

   sort_product   (Cabcd)
   rename_dummies (Cabcd)
   canonicalise   (Cabcd)                                                 # cdb (ex-13a.101,Cabcd)

   EightCabcd := 8 @(Cabcd).                                              # cdb (ex-13a.111,EightCabcd)

   gab := {g_{x x} = gxx, g_{x y} = gxy, g_{x z} = gxz,
           g_{y x} = gxy, g_{y y} = gyy, g_{y z} = gyz,
           g_{z x} = gxz, g_{z y} = gyz, g_{z z} = gzz}.

   complete  (gab, $g^{a b}$)
   evaluate  (Cabcd,gab)                                                  # cdb (ex-13a.102,Cabcd)
   evaluate  (EightCabcd,gab)                                             # cdb (ex-13a.112,EightCabcd)

\end{cadabra}

\clearpage

% \begin{dgroup*}
%    \Dmath*{ C_{abcd} = \Cdb*{ex-13a.100}
%                      = \Cdb*{ex-13a.101}
%                      = \Cdb*{ex-13a.102}}
% \end{dgroup*}

\begin{dgroup*}
   \Dmath*{ 8C_{abcd} = \Cdb*{ex-13a.110}
                      = \Cdb*{ex-13a.111}
                      = \Cdb*{ex-13a.112}}
\end{dgroup*}

\clearpage

% =============================================================================
\section*{Example 13b The Weyl tensor vanishes in 3d -- orthonormal basis}

\begin{cadabra}
   {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w#}::Indices(position=independent).

   g_{a b}::Metric.
   g^{a b}::InverseMetric.

   R_{a b c d}::RiemannTensor.

   ex{#}::LaTeXForm{"e_x"}.
   ey{#}::LaTeXForm{"e_y"}.
   ez{#}::LaTeXForm{"e_z"}.

   {R_{a b c d}, g_{a b}, g^{a b}}::SortOrder.

   Rab     := R_{a b} -> g^{c d} R_{a c b d}.

   Rscalar := R -> g^{a b} R_{a b}.

   gab := g^{a b} -> ex^{a} ex^{b} + ey^{a} ey^{b} + ez^{a} ez^{b}.

   ortho := {ex^{a} ex^{b} g_{a b} -> 1, ey^{a} ey^{b} g_{a b} -> 1, ez^{a} ez^{b} g_{a b} -> 1,
             ex^{a} ey^{b} g_{a b} -> 0, ex^{a} ez^{b} g_{a b} -> 0,
             ey^{a} ex^{b} g_{a b} -> 0, ey^{a} ez^{b} g_{a b} -> 0,
             ez^{a} ex^{b} g_{a b} -> 0, ez^{a} ey^{b} g_{a b} -> 0}.

   # Weyl in 3-dimensions

   Cabcd := R_{a b c d} - (R_{a c} g_{b d} - R_{a d} g_{b c})
                        - (g_{a c} R_{b d} - g_{a d} R_{b c})
                  + 1/2 R (g_{a c} g_{b d} - g_{a d} g_{b c}).   # cdb (ex-13b.100,Cabcd)

   substitute     (Cabcd, Rscalar)                               # cdb(ex-13b.101,Cabcd)
   substitute     (Cabcd, Rab)                                   # cdb(ex-13b.102,Cabcd)
   distribute     (Cabcd)                                        # cdb(ex-13b.103,Cabcd)

   Cabcd := C_{a b c d} -> @(Cabcd).

   Cxyxy := C_{a b c d} ex^{a} ey^{b} ex^{c} ey^{d}.             # cdb(ex-13b.104,Cxyxy)

   substitute     (Cxyxy,Cabcd)                                  # cdb(ex-13b.105,Cxyxy)
   distribute     (Cxyxy)                                        # cdb(ex-13b.106,Cxyxy)

   substitute     (Cxyxy, ortho, repeat=True)                    # cdb(ex-13b.107,Cxyxy)

   substitute     (Cxyxy, gab)                                   # cdb(ex-13b.108,Cxyxy)
   distribute     (Cxyxy)                                        # cdb(ex-13b.109,Cxyxy)

   sort_product   (Cxyxy)                                        # cdb(ex-13b.110,Cxyxy)
   rename_dummies (Cxyxy)                                        # cdb(ex-13b.111,Cxyxy)
   canonicalise   (Cxyxy)                                        # cdb(ex-13b.112,Cxyxy)
\end{cadabra}

\clearpage

\begin{dgroup*}
   \Dmath*{\cdb*{ex-13b.101}}
   \Dmath*{\cdb*{ex-13b.102}}
   \Dmath*{\cdb*{ex-13b.103}}
\end{dgroup*}

\clearpage

\begin{dgroup*}
   \Dmath*{\cdb{ex-13b.104}
         = \Cdb*[\hfill\hskip2.5cm]{ex-13b.105}
         = \Cdb*{ex-13b.106}
         = \Cdb*{ex-13b.107}
         = \Cdb*[\hfill\hskip2.0cm]{ex-13b.108}
         = \Cdb*{ex-13b.109}
         = \Cdb*{ex-13b.110}
         = \Cdb*{ex-13b.111}
         = \Cdb*{ex-13b.112}}
\end{dgroup*}

\clearpage

% =============================================================================
\section*{Example 13c The Weyl tensor vanishes in 3d -- orthonormal basis}

\begin{cadabra}
   Cxyxz := C_{a b c d} ex^{a} ey^{b} ex^{c} ez^{d}.             # cdb(ex-13c.101,Cxyxz)

   substitute     (Cxyxz,Cabcd)                                  # cdb(ex-13c.102,Cxyxz)

   distribute     (Cxyxz)                                        # cdb(ex-13c.103,Cxyxz)

   substitute     (Cxyxz, ortho, repeat=True)                    # cdb(ex-13c.104,Cxyxz)

   substitute     (Cxyxz, gab)                                   # cdb(ex-13c.105,Cxyxz)
   distribute     (Cxyxz)                                        # cdb(ex-13c.106,Cxyxz)

   sort_product   (Cxyxz)                                        # cdb(ex-13c.107,Cxyxz)
   rename_dummies (Cxyxz)                                        # cdb(ex-13c.108,Cxyxz)
   canonicalise   (Cxyxz)                                        # cdb(ex-13c.109,Cxyxz)
\end{cadabra}

\clearpage

\begin{dgroup*}
   \Dmath*{\cdb{ex-13c.101}
         = \Cdb*[\hfill\hskip2.5cm]{ex-13c.102}
         = \Cdb*{ex-13c.103}
         = \Cdb*{ex-13c.104}
         = \Cdb*{ex-13c.105}
         = \Cdb*{ex-13c.106}
         = \Cdb*{ex-13c.107}
         = \Cdb*{ex-13c.108}
         = \Cdb*{ex-13c.109}}
\end{dgroup*}

\clearpage

% ============================================================================================
% export to json format

\bgroup
\CdbSetup{action=hide}
\begin{cadabra}
   for i in range( len(checkpoint) ):
      cdblib.put ('check{:03d}'.format(i),checkpoint[i],checkpoint_file)
\end{cadabra}
\egroup

\end{document}


\subsection{k-means}
\begin{figure}
    \begin{minipage}{0.5\textwidth}
        \centering
        \includegraphics[width=.9\linewidth]{elbowds1.png}
        \caption{Timing curve vs silhouette coeff. dataset 1}\label{Fig:K-means vs silhouette coeff. dataset 1}
    \end{minipage}\hfill
    \begin{minipage}{0.5\textwidth}
        \centering
        \includegraphics[width=.9\linewidth]{elbowds2.png}
        \caption{Timing curve vs silhouette coeff. dataset 2}\label{Fig:K-means vs silhouette coeff. dataset 2}
    \end{minipage}
\end{figure}
The two clustering techniques I explored for these datasets are K-Means clustering and Gaussian Mixture Models (Expectation Maximization)
In order to choose an appropriate $k$ to do k-means clustering, I used the elbow method\cite{developers_2020}.
Unless otherwise specified the standard distance measure I used was Euclidean distance.
Comparing all cluster cohesion metrics (mean distortion, silhouette coefficient, and Calinski-Harabasz score) here were
the elbow results:
\begin{center}
    \begin{tabular}{|c| c | c | c |}
        \hline
        & Metric                     & Cluster Count & Value \\
        \hline
        \hline
        Dataset 1 & Avg Silhouette Coefficient & 4             & 0.558 \\
        \hline
        Dataset 2 & Avg Silhouette Coefficient & 3             & 0.687 \\
        \hline
    \end{tabular}
\end{center}
Looking at figure~\ref{Fig:K-means vs silhouette coeff. dataset 1} and figure~\ref{Fig:K-means vs silhouette coeff. dataset 2} you can
see similar numbers in terms of the best "k".
I chose silhouette coeff over the other cluster cohesion metrics because it has a bias towards preferring well separated
clusters (coeffs closer to 1), which I anticipate would improve generalization and overall accuracy during a machine
learning process where the clusters are inputs to the learner.
Dataset 2 appears to have better separation between its 3 clusters than dataset 1's 4 clusters.
Doing further silhouette analysis on DS1 reveals that the clusters when plotted on it to the surface do not appear to
have a "cluster" round shape and instead are ovular, whereas data set two had similar characteristics to data set one
except that cluster two is actually interspersed in between cluster zero and cluster one, however this was only for the
first two features and others could have had better separation/shape.
Both clustering methods On both data sets appeared to have two very strongly defined clusters, and then up to two additional weakly defined clusters.
This more or less aligns with the fact that this is a binary classification problem and that samples could be placed into one of two clusters.
The clusters were more well defined and well separated on the second dataset which also makes sense given that all supervised learning methods performed better in terms of accuracy on this dataset relative to the first.
On closer inspection, the third and fourth clusters appear to be randomly dispersed throughout the dataset to capture outliers.
These clusters likely are noise capturers.
\begin{center}
    \begin{tabular}{|c| c | c | c |}
        \hline
        & Normalized Mutual Information & Homogeneity Score & Completeness Score \\
        \hline
        \hline
        Dataset 1 & 0.001                         & 0.002             & 0.001              \\
        \hline
        Dataset 2 & 0.189                         & 0.236             & 0.158              \\
        \hline
    \end{tabular}
\end{center}
Values closer to 1 are better than 0 in the above table for each score metric.
It seems that the performance of K-means is slightly better on dataset 2 than on 1.
To improve the performance of this we would likely need to include a dimensionality reduction technique, or modify the
dataset so that clusters are more hyper-spherical and separated from each other in the feature space.

\subsection{Expectation Maximization}\label{subsec:expectation-maximization}
\begin{figure}
    \begin{minipage}{0.5\textwidth}
        \centering
        \includegraphics[width=.9\linewidth]{gmmcomponentsds1.png}
        \caption{n\_components vs AIC/BIC DS1}\label{Fig:GMM DS1}
    \end{minipage}\hfill
    \begin{minipage}{0.5\textwidth}
        \centering
        \includegraphics[width=.9\linewidth]{gmmcomponentsds2.png}
        \caption{n\_components vs AIC/BIC DS2}\label{Fig:GMM DS2}
    \end{minipage}
\end{figure}
For expectation maximization, I used the scikit-learn Gaussian mixture model function.
Based on my exploration, I do not believe that the data are distributed in a hyper-spherical format, therefore a GMM would
be able to better group samples based on its ability to accommodate non-spherical hyper-surfaces.
In order to select a best value for the number of estimators in the model, I plotted the Bayesian Information Criterion
vs the Akaike Information Criterion, where they begin to diverge is likely the best place to stop in terms of n\_estimators.
In this case looking at figures~\ref{Fig:GMM DS1} and~\ref{Fig:GMM DS2} it appears to be the same as the optimal K found
previously (4 and 3 respectively.) After finding these and running best fits and comparing results I got:
\begin{center}
    \begin{tabular}{|c| c | c | c |}
        \hline
        & Normalized Mutual Information & Homogeneity Score & Completeness Score \\
        \hline
        \hline
        Dataset 1 & 0.002                         & 0.002             & 0.002              \\
        \hline
        Dataset 2 & 0.133                         & 0.174             & 0.108              \\
        \hline
    \end{tabular}
\end{center}
These clusters more or less make sense much in the same way that K-means did.
For dataset 1, GMM outperformed in terms of mutual information, homogeneity, and completeness shared by the clustering techniques and the actual labels
whereas for dataset 2 K-means outperformed, which indicates that the data was slightly more spherical than I anticipated.
However, neither of these clustering techniques did very well since all the values were close to 0 indicating that assigment
to clusters was independent and that the clusters themselves were not very independent, nor complete.


\documentclass[journal]{IEEEtran}
\usepackage[utf8]{inputenc}
\usepackage{minted}
\usepackage{booktabs}
\usepackage{color}
\usepackage{mathtools}
\usepackage{mathrsfs}
\usepackage{multirow}

\usepackage[binary-units=true]{siunitx}

\newcommand{\scala}[1]{\mintinline{scala}{#1}}

\title{Advanced Algorithms (\texttt{LINGI2266}) \\ Assignment 1: Knapsack problem}
\author{Gilles Peiffer}
\date{October 11, 2019}

\begin{document}

\maketitle

\begin{abstract}
	An analysis of the solution to the knapsack problem which takes into account various dimensions along which to judge algorithms for this problem is given.
	Various heuristics are given, some of which are based on a theoretical understanding of the mathematics behind the knapsack problem, while others are highly-specialized in order to profit from the input formulation as much as possible.
	Some implementation fragments in Scala are also presented.
\end{abstract}

\section{Introduction}
\label{sec:intro}
The goal for this assignment was to solve various instances of the \emph{knapsack problem}, defined as follows:
\[
\begin{array}{rrcl@{\quad}l}
\textnormal{maximize} & \sum_{i \in \{1, \ldots, n\}} x_i v_i &&& \\
\textnormal{such that} & \sum_{i \in \{1, \ldots, n\}} x_i w_i & \le & C &\\
& x_i & \in & \{0, 1\} & \forall i \in \{1, \ldots, n\}
\end{array}
\]

The two main types of algorithms used to solve this class of problems are
\begin{itemize}
	\item \emph{dynamic programming} and
	\item \emph{branch and bound}.
\end{itemize}
Each of these has its own advantages and disadvantages, which usually allow one to make an informed choice as to which algorithm is best-suited for a particular instance of the knapsack problem.

\section{I/O}
In order to be easily tested on INGInious, the input and output for this particular instance have a very specific fixed format.
\subsection{Reading the Input}
The program is given a \scala{String} as input which contains the path to the data file.
This file is opened and read according to the format specified in the problem description on INGInious, and uses Scala code written for this purpose by Pierre Schaus.

\subsection{Output Formatting}
The \scala{toString} method definition is overridden in order to produce output with the correct format.
This requires some internal bookkeeping, explained in slightly more detail in Sections~\ref{sec:dpoutput} (for dynamic programming) and \ref{sec:bnboutput} (for branch and bound).

\section{Dynamic Programming}
Dynamic programming, or DP for short, is an algorithmic technique which uses memoization in order to improve computation time at the cost of memory space.

\subsection{Recurrence Equations}
One has to define an \emph{objective function} \(\mathscr{O}(j, k)\), which, for the knapsack problem, gives the optimal value that can be obtained using items 1 through \(j\) and with a capacity \(k\).
This function can be defined recursively as follows:
\[
\mathscr{O}(j, k) = \left\{
\begin{array}{ll}
\max\big\{ \mathscr{O}(j-1, k), & \multirow{2}{*}{\(\textnormal{if } w_j \le k,\)}\\
\qquad v_j + \mathscr{O}(j-1, k-w_j) \big\}, & \\\\
\mathscr{O}(j-1, k), & \textnormal{otherwise,}\\\\\\
0, & \textnormal{if } j = 0.
\end{array}
\right.
\]
By this definition, the final solution to the problem is \(\mathscr{O}(n, C)\).
\subsection{Implementation}
Turning these equations into Scala is fairly straightforward.
The following is what one could consider the ``core'' of the implementation:
\begin{minted}[fontsize=\scriptsize]{scala}
val cache = collection.mutable.Map.empty[(Int, Int), Int]
val tkn = collection.mutable.Map.empty[(Int, Int), Boolean]
def O(j: Int, k: Int): Int = {
  if (j < 0) 0
  else {
    val (vj, wj) = items(j)
    if (wj > k) {
      tkn.update((j, k), false)
      O_(j-1,k)
    } else {
      if (O_(j-1,k) > vj + O_(j-1,k-wj)) {
        tkn.update((j, k), false)
        O_(j-1,k)
      } else {
        tkn.update((j, k), true)
        vj + O_(j-1,k-wj)
      }
    }
  }
}
def O_(j: Int, k: Int): Int = {
  cache.getOrElseUpdate((j,k), O(j,k))
}
println(O(n-1, c))
\end{minted}
\subsubsection{Recurrence}
The recurrence equation given earlier is implemented in the form of a multitude of nested \scala{if}/\scala{else} branches, and a function call to \scala{O_}, which is explained in Section~\ref{sec:dpmemo}.
One should also note that instead of the solution being \(\mathscr{O}(n, C)\), one must call \scala{O(n-1, C)}; this is explained by the fact that in Scala, array indexing starts at 0, causing a potential off-by-one error that must be kept in mind.
\subsubsection{Memoization}
\label{sec:dpmemo}
The main goal of DP is to speed up computation time by storing values that were already computed--also called \emph{memoization}--and reusing those values whenever applicable.
In the code fragment, \scala{cache} is responsible for this: whenever a value of \scala{O}, the objective function, is needed, the program calls \scala{O_} which serves as an alias for \scala{cache.getOrElseUpdate}.
The latter's name describes what it does quite aptly: informally, it tells the computer to go and check if the value that is needed is already stored in \scala{cache}.
If it is, \scala{O_} simply returns that value; on the other hand, if it was not cached yet, it is computed (using the recursive property of the objective function) and the result is stored in \scala{cache} for future reference.
\subsubsection{Output Formatting}
\label{sec:dpoutput}
The \scala{Map} called \scala{tkn} stores whether the optimal solution for a given \scala{j} and \scala{k} uses the \scala{j}th element.
This is not related to the actual knapsack problem, but is necessary to be able to construct the output according to the specifications.

\subsection{Advantages and Disadvantages}
\subsubsection{Advantages}
DP has several advantages:
\begin{itemize}
	\item The algorithm is very easy to understand and easy to debug.
	\item \emph{For small values of \(C\)}, the algorithm is very fast and can very quickly solve problems which would be nigh impossible with a brute force search.
\end{itemize}
\subsubsection{Disadvantages}
On the other hand, there are some major downsides to the DP algorithm as well:
\begin{itemize}
	\item \emph{For large values of \(C\)}, the algorithm's pseudo-polynomiality makes it unusable.
	A variation based on the value \(V\) is also possible, but the same restrictions apply when \(V\) is large.
	This is for example what caused Instance B to take far too long using DP: for reference, while there are only 56 items to select, the maximum capacity and optimal value are both 104 723 596, meaning neither variation of the algorithm performs adequately.
\end{itemize}
Taking all of this into account, DP makes for a good first try at a solution, and can serve as an easy way to obtain solutions to a real-life problem to see whether other, more complex algorithms are correctly implemented and thought out.
For this assignment, two instances were solved using DP (D and E).

\section{Branch and Bound}
Branch and bound, or BnB for short, is an algorithm which uses upper-bounding and tree search in order to discard parts of a huge binary tree which have been shown not to contain an optimal solution.
More formally, consider the knapsack problem of Section~\ref{sec:intro}.
This problem, in a way, searches for a tuple of binary values which maximizes the objective function.
Consider the search tree formed by the possible value assignments to this tuple.
At the root, all variables are ``free'' to be either 1 or 0, but every time the tree branches in two, the left subtree has a variable set to 1, while the right subtree has that same variable set to 0.
If one has a procedure to efficiently find an upper bound for the subtree rooted at a certain node, the optimal solution can be obtained by first finding a feasible solution, and then trying to find a better one while discarding subtrees with an upper bound lower than or equal to the best known value of the feasible solution.
This way, one can avoid having to explore the entire search tree, whose size grows exponentially with the number of decision variables.

\subsection{Design Choices}
The explanation above is purposely vague, since many design choices still need to be taken based on the specific instance of the problem.
The most important is of the upper-bounding procedure.
While technically not necessary to find a solution, this is required to help discard subtrees faster and thus make the algorithm solve problems much more efficiently.
If the upper-bounding procedure is not sufficiently tight (or takes a long time to compute), branch and bound is not much better than a brute force approach.

\subsubsection{Upper-bounding}
The upper-bounding procedure of choice, which gives great results in the majority of cases, is called \emph{linear relaxation}.
What LR does is, instead of forcing variables to be binary, it allows them to be anywhere in \([0, 1]\).
By then sorting free items (i.e., whose decision variable has not been set yet) by their utility ratio, that is, the ratio between their value and weight, and selecting as many of these items (and potentially, fractions of items) as possible until the capacity constraint is tight, we can guarantee that the optimal objective will be lower than or equal to this upper bound.

To implement this, we do the following (suppose that \scala{selectable} has been \textbf{sorted} already):
\begin{minted}[fontsize=\scriptsize]{scala}
override val upperBound: Double = {
  // Linear relaxation.
  var i: Int = 0
  var ub: Double = obj
  var c: Int = capa
  if (c > 0) {
    while (i < selectable.length
        && c >= weight(selectable(i))) {
      c = c - weight(selectable(i))
      ub = ub + value(selectable(i))
      i += 1
    }
    if (i < selectable.length) {
      val d: Double = c.toDouble / weight(selectable(i))
      ub = ub + value(selectable(i)) * d
    }
  }
  ub
}
\end{minted}
In this code fragment, one can distinguish two main steps:
\begin{enumerate}
	\item first, use all the free variables (those in \scala{selectable}) in order to find the \emph{critical item} which would make the knapsack too full;
	\item add whatever you can of the critical item, maxing out the capacity.
\end{enumerate}

Additionally, though not necessary, the flooring function can be applied to this final result, since one can prove that if the weights and values are integers, then so is the optimal solution.

\subsubsection{Traversal algorithm}
This section mentions the traversal algorithms which can be used to decide which nodes are to be explored first.
Concretely, two options exist, each with their own advantages and disadvantages:
\begin{itemize}
	\item \emph{Best-first search}.
	This traversal looks at the most promising nodes (highest upper bound) first.
	This search strategy can be implemented with a \scala{PQueue}; the code used for this is heavily influenced by the one on the course's BitBucket repository, written by Pierre Schaus.
	The main advantage of this strategy is also its main drawback: it depends heavily on the quality of the upper-bounding procedure in order to be successful.
	Another big drawback is that the user has no control over the number of open nodes which are kept in memory.
	\item \emph{Depth-first search}.
	DFS looks at the deepest, leftmost node first, regardless of the upper bound (as long as exploring the subtree makes sense, i.e. as long as the subtree potentially contains an optimal solution).
	This can be implemented with a \scala{Stack}.
	This strategy, while not being as good in general at finding optimal solutions, does fare much better in the memory department, since memory consumption is proportional to the height of the tree, which is typically linear.
\end{itemize}
In the code, the only change that needs to be made is in the \scala{OpenNodes} implementation.

For this assignment, a best-first search algorithm using linear relaxation for upper bounding was used to solve instances A, C and F, which did either not get solved within the memory and time constraints by the DP algorithm (C) or because BnB was implemented before DP (A and F, where both algorithms arrive at the correct solution).

\subsection{Output formatting}
\label{sec:bnboutput}
When sorting the elements with the goal of being able to apply LR, the initial order gets lost.
This is quite problematic for this particular assignment, since the output format makes use of the initial order.
To get around this problem, one can add the original weight and value vectors to the \scala{KnapsackNode}, using these to identify used items.
The assumption that two items cannot be identical was verified beforehand, meaning this is a correct way for the instances of the assignment to recover the initial ordering.

\subsection{The Peculiar Case of Instance B}
Instance B has the property that all its items have a unit utility ratio.
This means that sorting them does nothing, other than potentially shuffle them (since quicksort is not a stable sort).
Add in the fact that the LR upper bound, while tight, gives little extra information in this case, and one quickly gets a problem which takes a very long time to get solved.

However, seeing as sorting by utility ratio does not change the ordering, and that the upper bound, despite being tight, does not allow best-first search to solve the problem optimally fast enough, an algorithm using DFS based on a \scala{Stack} was implemented, with elements being sorted based on their value (most valuable items being taken first).

This algorithm outperforms the other BnB one (and the DP solver as well) by quite some margin, finding the optimal solution in a matter of seconds (in fact, it happens to go down the ``optimal'' path almost immediately due to the order in which variables are examined).

\section{Conclusion}
The knapsack problem is one of the most important problems in combinatorial optimization, and DP and BnB are only some of the many possible ways to solve instances of it.
Thanks to this assignment, we learnt that in some cases, it is very hard to predict which algorithm is best-suited to a given instance.

Implementing the various algorithms helped to better understand why and how they work, while playing around with them on various instances helped to identify their strengths and shortcomings.
\end{document}


\subsection{Groups}\label{subsec:groups}

\begin{definition}\label{def:unital_magma_inverse_element}
  Let \( \mscrM \) be a \hyperref[def:unital_magma]{unital magma}. We say that \( y \) is the \term{left inverse} (resp. \term{right inverse}) of \( x \) if \( yx = e \) (resp. \( xy = e \)).

  If \( y \) is simultaneously a left and right inverse of \( x \), we call a \term{two-sided inverse} or simply an \term{inverse} of \( x \) and denote it by \( x^{-1} \). It is unique by \fullref{def:unital_magma_inverse_element_unique}. This notation is consistent with \fullref{def:unital_magma/exponentiation}
\end{definition}

\begin{proposition}\label{def:unital_magma_inverse_element_unique}
  For every element \( x \) of any unital magma, the (two-sided) \hyperref[def:unital_magma_inverse_element]{inverse} \( x^{-1} \) of \( x \) is unique.
\end{proposition}
\begin{proof}
  If \( y \) and \( z \) are both inverses of \( x \), then
  \begin{equation*}
    y = ey = zxy = ze = z.
  \end{equation*}
\end{proof}

\begin{definition}\label{def:group}
  A \term{group} is a \hyperref[def:unital_magma/monoid]{monoid} in which every element has an \hyperref[def:unital_magma_inverse_element]{inverse}. Groups are the most well-studied and most well-behaved magmas. Many useful properties like \hyperref[thm:def:group/properties/cancellative]{cancellation} rely on associativity, so we do not consider non-associative groups.

  \begin{thmenum}
    \thmitem{def:group/theory} We can construct the \hyperref[def:first_order_theory]{theory of groups} by adding a unary \hyperref[def:first_order_language/func]{functional symbol} \( (\anon)^{-1} \) and the axiom
    \begin{equation}\label{eq:def:group/theory/inverse_axiom}
      \qforall \xi (\xi \cdot \xi^{-1} = e \wedge \xi^{-1} \cdot \xi = e)
    \end{equation}
    to the theory of \hyperref[def:unital_magma/monoid]{monoids}.

    \thmitem{def:group/function_parity} A \hyperref[def:function]{function} \( \varphi: \mscrG \to \mscrH \) between two groups is called \term{even} if, for every \( x \in \mscrG \), we have
    \begin{equation}\label{eq:def:group/function_parity/even}
      \varphi(x^{-1}) = \varphi(x)
    \end{equation}
    and \term{odd} if
    \begin{equation}\label{eq:def:group/function_parity/odd}
      \varphi(x^{-1}) = \varphi(x)^{-1}.
    \end{equation}

    \thmitem{def:group/homomorphism} A \hyperref[def:first_order_homomorphism]{first-order homomorphism} between the groups \( \mscrG \) and \( \mscrH \) is an odd \hyperref[def:unital_magma/homomorphism]{unital magma homomorphism}.

    As shown in \fullref{thm:group_homomorphism_single_condition}, however, the conditions \eqref{eq:def:pointed_set/homomorphism} and \eqref{eq:def:group/function_parity/odd} are redundant.

    \thmitem{def:group/submodel} The set \( S \subseteq \mscrG \) is a \hyperref[def:first_order_substructure]{first-order substructure} of \( \mscrG \) if it is a \hyperref[def:unital_magma/submodel]{unital submagma} and if \( S^{-1} = S \), where
    \begin{equation*}
      S^{-1} = \set{ s^{-1} \given s \in S }.
    \end{equation*}

    \thmitem{def:group/trivial}  The \hyperref[thm:substructures_form_complete_lattice/bottom]{trivial} group is the \hyperref[def:pointed_set/trivial]{trivial pointed set} \( \set{ e } \).

    \thmitem{def:group/category} The \hyperref[def:category_of_small_first_order_models]{category of \( \mscrU \)-small models} of groups \( \ucat{Grp} \) is \hyperref[def:concrete_category]{concrete} with respect to \hyperref[def:unital_magma/monoid]{\( \ucat{Mon} \)}.

    \thmitem{def:group/exponentiation} We extend \hyperref[def:unital_magma/exponentiation]{unital magma exponentiation} to all integers by setting
    \begin{equation*}
      x^{-n} \coloneqq (x^n)^{-1}.
    \end{equation*}

    This operation behaves well as shown in \fullref{thm:def:group/properties/negative_power}.
  \end{thmenum}
\end{definition}

\begin{proposition}\label{thm:group_homomorphism_single_condition}
  A function \( \varphi: \mscrG \to \mscrH \) between the groups \( \mscrG \) and \( \mscrH \) is a \hyperref[def:group/homomorphism]{homomorphism} if and only if it satisfies \eqref{eq:def:magma/homomorphism}.
\end{proposition}
\begin{proof}
  \SufficiencySubProof \eqref{eq:def:magma/homomorphism} is required to hold by definition.

  \NecessitySubProof Let the function \( \varphi \) satisfy \eqref{eq:def:magma/homomorphism}. Then it preserves identities (i.e. is a \hyperref[def:pointed_set/homomorphism]{pointed set homomorphism}) since
  \begin{equation*}
    e_{\mscrH} \varphi(e_{\mscrG}) = \varphi(e_{\mscrG}) = \varphi(e_{\mscrG} e_{\mscrG}) = \varphi(e_{\mscrG}) \varphi(e_{\mscrG})
  \end{equation*}
  and by \fullref{thm:def:group/properties/cancellative}, the operation is cancellative.

  Inverses are preserved (i.e. \eqref{eq:def:group/function_parity/odd} holds) because
  \begin{equation*}
    \varphi(x^{-1})
    =
    \varphi(x^{-1}) e_{\mscrH}
    =
    \varphi(x^{-1}) \varphi(x) \varphi(x)^{-1}
    =
    \varphi(x^{-1} x) \varphi(x)^{-1}
    =
    e_{\mscrH} \varphi(x)^{-1}
    =
    \varphi(x)^{-1}.
  \end{equation*}

  Therefore, \( \varphi \) is indeed a group homomorphism.
\end{proof}

\begin{proposition}\label{thm:def:group/properties}
  Every \hyperref[def:group]{group} \( \mscrG \) has the following basic properties:
  \begin{thmenum}
    \thmitem{thm:def:group/properties/cancellative} The (binary) group operation is \hyperref[def:magma/cancellative]{cancellative}.
    \thmitem{thm:def:group/properties/identity_inverse} The identity \( e \) is its own inverse.
    \thmitem{thm:def:group/properties/inverse_composition} \( (xy)^{-1} = y^{-1} x^{-1} \).
    \thmitem{thm:def:group/properties/double_inverse} \( x = (x^{-1})^{-1} \)
    \thmitem{thm:def:group/properties/negative_power} For any positive integer \( n \), \( (x^n)^{-1} = (x^{-1})^n \)
  \end{thmenum}
\end{proposition}
\begin{proof}
  \SubProofOf{thm:def:group/properties/cancellative} If \( x = y \), obviously \( xz = yz \) and \( zx = zy \). Now if \( xz = yz \), we have
  \begin{equation*}
    x = x(zz^{-1}) = (xz)z^{-1} = (yz)z^{-1} = y(zz^{-1}) = y.
  \end{equation*}

  The case \( zx = zy \) is analogous.

  \SubProofOf{thm:def:group/properties/identity_inverse} \( ee = e \).
  \SubProofOf{thm:def:group/properties/inverse_composition}
  \begin{equation*}
    (xy) (y^{-1} x^{-1})
    =
    x (y y^{-1}) x^{-1}
    =
    e
    =
    y^{-1} (x^{-1} x) y
    =
    (y^{-1} x^{-1}) (xy).
  \end{equation*}

  \SubProofOf{thm:def:group/properties/double_inverse}
  \begin{equation*}
    (x^{-1})^{-1}
    =
    x x^{-1} (x^{-1})^{-1}
    =
    x.
  \end{equation*}

  \SubProofOf{thm:def:group/properties/negative_power} Using \fullref{thm:def:group/properties/double_inverse},
  \begin{equation*}
    x^{-n}
    =
    (x^n)^{-1}
    =
    x^{-1} \cdots x^{-1}
    =
    (x^{-1})^n.
  \end{equation*}
\end{proof}

\begin{example}\label{ex:power_set_is_not_a_group}
  The \hyperref[def:magma/power_set]{power set magma} \( \pow(\mscrG) \) of a group \( \mscrG \) is a monoid, but it is not a group unless \( \mscrG \) is trivial.
\end{example}

\begin{definition}\label{def:group_cosets}
  Let \( \mscrH \subseteq \mscrG \) be a subgroup of \( \mscrG \) and let \( x \in \mscrG \). The sets
  \begin{equation*}
    x \mscrH \coloneqq \set{ xh \colon h \in \mscrH }
    \quad\quad
    \mscrH x \coloneqq \set{ hx \colon h \in \mscrH }
  \end{equation*}
  are called the left and right \term{cosets} of \( \mscrH \) with respect to \( x \). The name is justified by \fullref{thm:group_coset_partition}.

  The \hyperref[def:cardinal]{cardinality} of the set of all left cosets is called the \term{index} of \( \mscrH \) and is denoted by \( [\mscrG : \mscrH] \). By \fullref{thm:lagranges_theorem_for_groups}, the index can analogously be defined as the cardinality of all right cosets.
\end{definition}

\begin{lemma}\label{thm:group_coset_partition}
  The \hyperref[def:group_cosets]{left cosets} of a subgroup of \( \mscrG \) \hyperref[def:set_partition]{partition} \( \mscrG \). The same holds for right cosets.
\end{lemma}
\begin{proof}
  To each element \( x \in \mscrG \) there corresponds a coset \( x \in x\mscrH \) (since, as a subgroup, \( \mscrH \) contains the identity).

  Two cosets \( x\mscrH \) and \( y\mscrH \) are either disjoint or equal. Indeed, if they are not disjoint, then there exists \( z \in x\mscrH \cap y\mscrH \) and thus \( z = xa = yb \) for some \( a, b \in \mscrH \). Thus,
  \begin{equation*}
    x = x a a^{-1} = y b a^{-1}
  \end{equation*}
  and since \( b a^{-1} \in \mscrH \), we have that \( x \in y\mscrH \). Furthermore, for any \( c \in \mscrH \), we have
  \begin{equation*}
    xc = y(b a^{-1} c) \in y\mscrH,
  \end{equation*}
  hence \( x\mscrH \subseteq y\mscrH \). After obtaining the converse inclusion, we conclude \( x\mscrH = y\mscrH \).
\end{proof}

\begin{lemma}\label{thm:group_coset_bijection}
  Any two left cosets in a group are \hyperref[def:equinumerosity]{equinumerous}. The same holds for right cosets.
\end{lemma}
\begin{proof}
  Let \( \mscrH \) be a subgroup of \( \mscrG \) and let \( x, y \in \mscrG \). Then \( z \mapsto y x^{-1} z \) sends \( x\mscrH \) into \( y\mscrH \). By \fullref{thm:group_multiplication_is_bijection}, this function is a bijection.
\end{proof}

\begin{definition}\label{def:normal_subgroup}
  Let \( \mscrN \) be a subgroup of \( \mscrG \). We say that \( \mscrN \) is a normal subgroup if any of the following equivalent conditions hold:
  \begin{thmenum}
    \thmitem{def:normal_subgroup/direct} For every element \( x \) of \( \mscrG \), we have the set equality
    \begin{equation}\label{eq:def:normal_subgroup/direct}
      x \mscrN x^{-1} = \mscrN.
    \end{equation}

    \thmitem{def:normal_subgroup/cosets} The partitions induced by the left and rights cosets of \( \mscrN \) coincide.
    \thmitem{def:normal_subgroup/kernel} \( \mscrN \) is the \hyperref[def:pointed_set_kernel]{kernel} of some group homomorphism.
  \end{thmenum}

  In particular, kernels are always normal subgroups.
\end{definition}
\begin{proof}
  This is the group-theoretic analog to \fullref{thm:equivalence_partition}.

  \ImplicationSubProof{def:normal_subgroup/direct}{def:normal_subgroup/cosets} For any \( x \in \mscrG \)
  \begin{equation*}
    \mscrN x = (x \mscrN x^{-1})x = x \mscrN(x^{-1}x) = x \mscrN,
  \end{equation*}
  thus every left coset is a right coset and vice versa.

  \ImplicationSubProof{def:normal_subgroup/cosets}{def:normal_subgroup/kernel} We can take the \hyperref[def:quotient_group]{canonical projection} \( \pi(x) \coloneqq x \mscrN \) as the homomorphism. The proof of correctness in \fullref{def:quotient_group} only uses \fullref{def:normal_subgroup/cosets} and therefore does not cause circular references.

  \ImplicationSubProof{def:normal_subgroup/kernel}{def:normal_subgroup/direct} Let \( \varphi: \mscrG \to \mscrH \) be a group homomorphism and fix any \( x \in \mscrG \). Denote \( \mscrN \coloneqq \ker(f) \). Then \( x \mscrN = \mscrN x \) since
  \begin{equation*}
    \varphi(x \mscrN)
    =
    \varphi(x) \varphi(\mscrN)
    =
    \varphi(x) \varphi(e_{\mscrG})
    =
    \varphi(x)
    =
    \varphi(\mscrN) \varphi(x)
    =
    \varphi(\mscrN x)
    =
    e_{\mscrH}.
  \end{equation*}

  Thus,
  \begin{equation*}
    \varphi^{-1}(e_{\mscrH}) = \mscrN = xx^{-1}\mscrN = x \mscrN x^{-1}.
  \end{equation*}
\end{proof}

\begin{definition}\label{def:quotient_group}
  Let \( \mscrG \) be a group and \( \mscrN \) be a normal subgroup of \( \mscrG \). Define the \term{quotient group}
  \begin{equation*}
    \mscrG / \mscrN \coloneqq \{ x \mscrN \colon x \in \mscrG \}
  \end{equation*}
  with the group operation
  \begin{equation*}
    x \mscrN \star y \mscrN \coloneqq xy \mscrN.
  \end{equation*}

  Define the canonical projection homomorphism
  \begin{align*}
    &\pi: \mscrG \to \mscrG / \mscrN \\
    &\pi(x) \coloneqq x \mscrN.
  \end{align*}

  The kernel of \( \pi \) is precisely \( \mscrN \).
\end{definition}
\begin{proof}
  This definition is used in the proof of equivalence in \fullref{def:normal_subgroup}. This is why it is important to only use \fullref{def:normal_subgroup/cosets} as the definition for a normal subgroup.

  We first check that the group operations is well-defined, that is, does not depend on the choice of coset representatives. Fix \( x_1, x_2 \in \mscrG \) and \( y_1, y_2 \in \mscrG \), so that
  \begin{equation*}
    x_1 \mscrN = y_1 \mscrN
  \end{equation*}
  and
  \begin{equation*}
    x_2 \mscrN = y_2 \mscrN.
  \end{equation*}

  Since the left and right cosets coincide, we have
  \begin{equation*}
    x_1 x_2 \mscrN = x_1 \mscrN x_2 = x_1 \mscrN y_2 = y_1 \mscrN y_2 = y_1 y_2 \mscrN.
  \end{equation*}

  Thus, the operation is well-defined.

  It follows from the definition that the identity is \( e \mscrN = \mscrN \) and the inverse of \( x \mscrN \) is \( x^{-1} \mscrN \). Therefore, \( \mscrG / \mscrN \) is indeed a group. The fact that \( \pi \) is a homomorphism is also part of the definition of \( \star \).

  It remains to prove that \( \mscrN = \ker(\pi) \). Obviously \( \pi(\mscrN) = \mscrN \), so \( \mscrN \subseteq \ker(\pi) \). To see that the converse holds, assume that there exists \( x \in \ker(\pi) \setminus \mscrN \), i.e. \( \pi(x) = x\mscrN = \mscrN \), but \( x \not\in N \). Then there exists \( y \in N \) such that \( xy \in N \). The subgroup \( \mscrN \) is closed under multiplication and inverses, hence \( x = xyy^{-1} \in N \). This contradicts our assumption that \( x \not\in N \). Therefore, \( \mscrN = \ker \pi \).
\end{proof}

\begin{theorem}[Lagrange's theorem for groups]\label{thm:lagranges_theorem_for_groups}
  Let \( \mscrH \) be a subgroup of \( \mscrG \). We have the following equality
  \begin{equation}\label{eq:thm:lagranges_theorem_for_groups/index}
    \card(\mscrG) = \card(\mscrH) \cdot [\mscrG : \mscrH].
  \end{equation}

  If \( \mscrH \) is a \hyperref[def:normal_subgroup]{normal subgroup}, then \( [\mscrG : \mscrH] = \card(\mscrG / \mscrH) \) and
  \begin{equation}\label{eq:thm:lagranges_theorem_for_groups/card}
    \card(\mscrG) = \card(\mscrH) \cdot \card(\mscrG / \mscrH).
  \end{equation}

  This demonstrates that there exists a bijective function between the \hyperref[def:group_direct_product]{direct product} \( \mscrH \times \mscrG / \mscrH \) and \( \mscrH \), however this may not be a group homomorphism --- see \fullref{ex:lagranges_theorem_for_groups/direct_product_zn}.
\end{theorem}
\begin{proof}
  Follows from \fullref{thm:group_coset_partition} and \fullref{thm:group_coset_bijection}
\end{proof}

\begin{theorem}[Homomorphism theorem for groups]\label{thm:homomorphism_theorem_for_groups}
  For any \hyperref[def:group/homomorphism]{group homomorphism} \( \varphi: \mscrG \to \mscrH \), we have the isomorphism
  \begin{equation*}
    \mscrG / \ker \varphi \cong \img \varphi.
  \end{equation*}
\end{theorem}
\begin{proof}
  Denote \( \mscrN \coloneqq \ker \varphi \). Define the function
  \begin{align*}
    &\psi: \img \varphi \to \pow(\mscrG) \\
    &\psi(y) \coloneqq \varphi^{-1}(y) \mscrN.
  \end{align*}

  We will show that \( \psi \) is the desired isomorphism.

  Fix any \( y \in \img \varphi \) and \( x_1, x_2 \in \varphi^{-1}(y) \). We will first show that \( x_1 \mscrN = x_2 \mscrN \). Note that
  \begin{equation*}
    \varphi(x_1^{-1} x_2)
    =
    \varphi(x_1)^{-1} \varphi(x_2)
    =
    \varphi(x_2)^{-1} \varphi(x_2)
    =
    e_{\mscrH},
  \end{equation*}
  therefore \( x_1^{-1} x_2 \in N \). Thus,
  \begin{equation*}
    x_2 \mscrN = x_1 x_1^{-1} x_2 \mscrN = x_1 \cdot \mscrN \cdot \mscrN = x_1 \mscrN.
  \end{equation*}

  Hence, \( \varphi^{-1}(y) \mscrN \) is a coset in \( \mscrG / \mscrN \) formed by any of the elements of \( \varphi^{-1}(y) \).

  Furthermore, if \( x_1 \in x_2 \mscrN \), then there exists \( n \in N \) such that
  \begin{equation*}
    x_1 = x_2 n.
  \end{equation*}

  But \( \mscrN \) is closed under taking inverses, hence
  \begin{equation*}
    x_2 = x_1 n^{-1} \in x_1 \mscrN,
  \end{equation*}
  that is,
  \begin{equation*}
    x_1 \mscrN = x_2 \mscrN.
  \end{equation*}

  This shows that \( \psi \) is injective. It is obviously surjective because if \( x \mscrN \) is a coset, then \( \varphi(x) \in \img \varphi \). Therefore, \( \varphi \) is bijective.

  It remains to show that \( \psi \) is a homomorphism. Indeed, if \( y_1, y_2 \in \img \varphi \) and
  \begin{equation*}
    x_k \in \varphi^{-1}(y_k), k = 1, 2,
  \end{equation*}
  we have
  \begin{balign*}
    \psi(y_1) \psi(y_2)
    &=
    \varphi^{-1}(y_1) \mscrN \varphi^{-1}(y_2) \mscrN
    = \\ &=
    x_1 \mscrN x_2 \mscrN
    \reloset {\eqref{eq:def:normal_subgroup/direct}} = \\ &=
    (x_1 x_2) \mscrN
    = \\ &=
    \varphi^{-1}(y_1 y_2) \mscrN
    = \\ &=
    \psi(y_1 y_2).
  \end{balign*}
\end{proof}


\newpage
\section{Roots Package}
\begin{Introduction}{Roots Package}
\index{roots}\index{polynomial}
The root  finding package is designed  so that it can
be  used to  find some  or  all of  the roots  of  univariate
polynomials  with  real or  complex  coefficients,  to  the  accuracy
specified by the user.

Not all operators of \name{roots package} are described here. For using
the operators

\nameindex{isolater} (intervals isolating real roots)

\nameindex{rlrootno} (number of real roots in an interval)

\nameindex{rootsat-prec} (roots at system precision)

\nameindex{rootval} (result in equation form)

\nameindex{firstroot} (computing only one root)

\nameindex{getroot} (selecting roots from a collection)

please consult the full documentation of the package.

\end{Introduction}

\begin{Operator}{MKPOLY}
\index{polynomial}\index{roots}\index{interpolation}
Given a roots list as returned by \nameref{roots},
the operator \name{mkpoly} constructs a
polynomial which has these numbers as roots.
\begin{Syntax}
\name{mkpoly} \meta{rl}
\end{Syntax}
where \meta{rl} is a \nameref{list} with equations, which
all have the same \nameref{kernel} on their left-hand sides
and numbers as right-hand sides.

\begin{Examples}
mkpoly{x=1,x=-2,x=i,x=-i};&
x**4 + x**3 - x**2 + x - 2\\
\end{Examples}

Note that this polynomial is unique only up to a numeric
factor.
\end{Operator}

\begin{Operator}{NEARESTROOT}
\index{roots}\index{solve}
The operator \name{nearestroot} finds one root of a polynomial
with an iteration using a given starting point.

\begin{Syntax}
\name{nearestroot}\(\meta{p}\,\meta{pt}\)
\end{Syntax}

where \meta{p} is a univariate polynomial
and \meta{pt} is a number.

\begin{Examples}
nearestroot(x^2+2,2);&\{x=1.41421*i\}\\
\end{Examples}
The minimal accuracy of the result values is controlled by
\nameref{rootacc}.
\end{Operator}

\begin{Operator}{REALROOTS}
\index{roots}\index{solve}
The operator \name{realroots} finds that real roots of a polynomial
to an accuracy that is sufficient to separate them  and which is
a minimum of  6 decimal places.

\begin{Syntax}
\name{realroots}\(\meta{p}\) or \\
\name{realroots}\(\meta{p}\,\meta{from},\meta{to}\)
\end{Syntax}

where \meta{p} is a univariate polynomial.
The optional parameters \meta{from} and \meta{to} classify
an interval: if given, exactly the real roots in this
interval will be returned. \meta{from} and \meta{to}
can also take the values \name{infinity} or \name{-infinity}.
If omitted all real roots will be returned.
Result is a \nameref{list}
of equations which represent the roots of the polynomial at the
given accuracy.

\begin{Examples}
realroots(x^5-2);&\{x=1.1487\}\\
realroots(x^3-104*x^2+403*x-300,2,infinity);&\{x=3.0,x=100.0\}\\
realroots(x^3-104*x^2+403*x-300,-infinity,2);&\{x=1\}\\
\end{Examples}
The minimal accuracy of the result values is controlled by
\nameref{rootacc}.
\end{Operator}

\begin{Operator}{ROOTACC}
\index{roots}\index{accuracy}
The operator \name{rootacc} allows you to set the accuracy
up to which the roots package computes its results.
\begin{Syntax}
\name{rootacc}\(\meta{n}\)
\end{Syntax}
Here \meta{n} is an integer value. The internal accuracy of
the \name{roots} package is adjusted to a value of
\name{max(6,n)}. The default value is \name{6}.
\end{Operator}

\begin{Operator}{ROOTS}
\index{roots}\index{solve}\index{polynomial}
The operator \name{roots}
is  the main top level  function of the roots  package.
It will  find all roots, real and  complex, of the polynomial  p
to an accuracy that is sufficient to separate them  and which is
a minimum of  6 decimal places.

\begin{Syntax}
\name{roots}\(\meta{p}\)
\end{Syntax}

where \meta{p} is a univariate polynomial. Result is a \nameref{list}
of equations which represent the roots of the polynomial at the
given accuracy. In addition, \name{roots}  stores
separate  lists of real  roots and complex  roots in the  global
variables \nameref{rootsreal} and \nameref{rootscomplex}.

\begin{Examples}
roots(x^5-2);&\begin{multilineoutput}{3cm}
\{x=-0.929316 + 0.675188*i,
  x=-0.929316 - 0.675188*i,
  x=0.354967 + 1.09248*i,
  x=0.354967 - 1.09248*i,
  x=1.1487\}
\end{multilineoutput}\\
\end{Examples}
The minimal accuracy of the result values is controlled by
\nameref{rootacc}.
\end{Operator}

\begin{Operator}{ROOT\_VAL}
\index{roots}\index{solve}\index{polynomial}
The operator \name{root\_val} computes the roots of a
univariate polynomial at system precision
(or greater if required for root separation) and presents
its result as a list of numbers.
\begin{Syntax}
\name{roots}\(\meta{p}\)
\end{Syntax}

where \meta{p} is a univariate polynomial.

\begin{Examples}
root_val(x^5-2);&\begin{multilineoutput}{3cm}
\{-0.929316490603 + 0.6751879524*i,
 -0.929316490603 - 0.6751879524*i,
 0.354967313105 + 1.09247705578*i,
 0.354967313105 - 1.09247705578*i,
 1.148698355\}
\end{multilineoutput}\\
\end{Examples}
\end{Operator}

\begin{Variable}{ROOTSCOMPLEX}
\index{roots}\index{complex}
When the operator \nameref{roots} is called the complex
roots are collected in the global variable \name{rootscomplex}
as \nameref{list}.
\end{Variable}

\begin{Variable}{ROOTSREAL}
\index{roots}\index{complex}
When the operator \nameref{roots} is called the real
roots are collected in the global variable \name{rootreal}
as \nameref{list}.
\end{Variable}


\documentclass[../../main.tex]{subfiles}
\begin{document}
\subsubsection{Remove}
The inverse of the $append$ Operations.
\begin{zed}
  remove(coll, idx) = \inv append(coll, idx)
\end{zed}
The operation $remove$ will return a Collection minus the Value removed from the specified Numeric Index
\begin{schema}{Remove[Collection, \nat]}
  coll?, coll! : Collection \\
  idx? : \nat \\
  remove~\_ : Collection \cross \nat \surj Collection
  \where
  \#~ idx? = 1 \\
  coll! = remove(coll?, idx?) @ \\
  \t1 let ~ ~ coll' == front(\{~ i : \nat ~| ~ i \in 0~..~idx?\} \extract coll?) \\
  \t1 \ \ \ ~~~ coll'' == tail(\{~ j : \nat ~| ~ j \in idx?~..~\#~coll?\} \extract coll?) \\
  \t1 = coll' \cat coll'' \implies \\
  \t2 (coll?_{idx?} \not \in coll') ~\land \\
  \t2 (coll?_{idx?} \not \in coll'') ~\land \\
  \t2 (~\#~ coll! = \#~ coll? - 1)
\end{schema}
such that
\begin{argue}
  X = \langle x_{0}, x_{1}, x_{2} \rangle \\
  \t1 x_{0} = 0 \\
  \t1 x_{1} = foo \\
  \t1 x_{2} = baz \\
  remove(X, 0) = \langle foo, baz \rangle & 0 was removed from $X$\\
  remove(X, 1) = \langle 0, baz \rangle & foo was removed from $X$ \\
  remove(X, 2) = \langle 0, foo \rangle & baz was removed from $X$ \\
  remove(X, 3) = \langle 0, foo, baz \rangle = X & nothing at 3, X unaltered
\end{argue}
\end{document}


\documentclass{article}
\usepackage{mathrsfs,amsmath}
\usepackage{xcolor}
\usepackage{titlesec}
\usepackage{listings}
\usepackage{syntax}
\usepackage{pythonhighlighting}
\usepackage{fancyvrb}

\usepackage[margin=1.4in]{geometry}

\title{Homework \#1 | Fall 2021}
\author{Jared Dyreson\\
        California State University, Fullerton}

\DeclareRobustCommand{\bowtie}{%
  \mathrel\triangleright\joinrel\mathrel\triangleleft}

\usepackage [english]{babel}
\usepackage [autostyle, english = american]{csquotes}
\MakeOuterQuote{"}

\titlespacing*{\section}
{0pt}{5.5ex plus 1ex minus .2ex}{4.3ex plus .2ex}
\titlespacing*{\subsection}
{0pt}{5.5ex plus 1ex minus .2ex}{4.3ex plus .2ex}

\usepackage{hyperref}
\hypersetup{
    colorlinks,
    citecolor=black,
    filecolor=black,
    linkcolor=black,
    urlcolor=black
}

\begin{document}

\maketitle
\tableofcontents

\newpage

\section{Questions}

\begin{enumerate}

\item Order the following functions by asymptotic growth rate: \\

$$4n, 2^{\log(n)}, 4n \times \log(n) + 2n, 2^{10}, 3n + 100\times \log(n) \times, 2^{n}, n^{2} + 10n, n^{3}, n \times \log(n)$$ \\
State the asymptotic growth rate for each of the functions in terms of Big-Oh and explicitly order them from least to greatest.

\begin{itemize}
\item $2 ^ {10} \implies O(1)$. Running time is independent of the input size.
\item $2^{\log(n)} \implies O(n)$ ? I would assume the log and 2 would cancel each other out in an event like this: $2^{\log_2(n)}$. Unsure about this one.
\item $3n + 100 \times log(n) \implies O(n)$
\item $4n \implies O(n)$
\item $n \times \log(n) \implies O(n \times \log(n))$
\item $4n \times \log(n) + 2n \implies O(n \times \log(n))$
\item $n^{2} + 10n \implies O(n^{2})$
\item $n^{3} \implies O(n^{3})$
\item $2^{n} \implies O(2^{n}) \rightarrow$ exponential.
\end{itemize}

\item Write a problem definition and pseudocode for each of the following problems.
\begin{itemize}
\item Computing a square root
\begin{table}[!htbp]
\centering
\begin{tabular}{|l|}
\hline
\textit{computing a square root} \\ \hline
\textbf{input}: an real integer that is greater than or equal to 0 \\ \hline
\textbf{output}: a floating point integer that is the resultant of a square root operation \\ \hline
\end{tabular}
\end{table}

\begin{python}
# Compute square root
def square_root_function(value):
    # Driver code should recognize that -1 denotes failure
    if value is less than 0, then return -1
    else return value ** 0.5 # value raised to the 1/2 power
\end{python}

\item Determining whether an integer is even or odd

\begin{table}[!htbp]
\centering
\begin{tabular}{|l|}
\hline
\textit{determine whether integer is even or odd} \\ \hline
\textbf{input}: a real integer that is greater than or equal to 0 \\ \hline
\textbf{output}: a boolean value denoting status (true for even, false for odd) \\ \hline
\end{tabular}
\end{table}

\begin{python}
# Check if integer is even or odd
def even_or_odd(value):
    # even values should not have a remainder, thus equal to 0
    return (value % 2 == 0)

\end{python}

\newpage

\item Determining whether every element in a sequence is identical

\begin{table}[!htbp]
\centering
\begin{tabular}{|l|}
\hline
\textit{determine whether all sequence elements are identical} \\ \hline
\textbf{input}: a non-empty sequence that contains elements that are comparable with one another \\ \hline
\textbf{output}: a boolean value denoting status of homogeneity \\ \hline
\end{tabular}

\end{table}

\begin{python}
# Check if sequence has all the same elements
def contains_same_elements(container: list[int]):
    if container is empty:
        return None
    top_most = container[0]
    x = 0
    for element in container:
        if(container[x] != top_most)
            return False
        top_most = container[x]
        x = x + 1
    return True
\end{python}

\end{itemize}

\item Consider the following algorithm:
\begin{Verbatim}
Algorithm Calc(a, n):
    Input: two integers, a and n
    Output: ?
    k <- 0
    b <- 1
    while k < n do
        k <- k + 1
        b <- b * a
    return b
\end{Verbatim}

\begin{python}
def calc(a: int, n: int) -> int:
    k, b = 0, 1
    while(k < n):
        k += 1
        b *= a
        # two atomics, this is what a for loop is when broken down into smaller components
    return b
\end{python}

\begin{itemize}
\item What does the algorithm calculate?: The value of $b$ continuously multiplied by a factor of $a$, $n$ times. $k$ is simply an index counter, and this entire expression can be written as a for loop:
\begin{python}
def calc(a: int, n: int) -> int:
    b = 1 # 1
    for _ in range(0, n): # one atomic
        b *= a
        # two atomics
    return b
\end{python}
\item Analyze its worst-case running time and express it using Big-Oh notation: Well we can tell that this function will at max run $n$ times. Coutning each instruction in this algorithm will allow us to determine that $T(n) = 3n + 3$. We can assume this is a linear operation, as seen in the for/while loop condition. Thus classifying it as $O(n)$.
\end{itemize}

\end{enumerate}

\end{document}


\section{Unknown Values}
\label{unknowns}

\begin{quote}
When you come to a fork in the road, take it \footnote{attributed to Yogi Berra}
\end{quote}
Assume that we have \(X\) instances of which the fraction which have class
``Heads'' is \(p_H\) and Tails is \(p_T\).
Assume that we are considering splitting using
a feature \(f\) such that \(X' \subseteq X\) of the instances have unknown
values for this feature.
the form \(x \leq y\).

In terms of the training process, unknown values affect the process
as follows.  With each instance \(x_i \in X\) is associated a weight \(w_i\),
initialized to 1. Let \(X'\) be the instances where the value of the splitting feature is unknown.

\be
\item Benefit benefit calculation of Section~\ref{XXX}.
For all \(x_i \in X'\), we treat it as if it were two instances, one with a class of Heads and a weight of \(w_i \times p_H\) and the other with a class of Tails and a weight of \(w_i \times p_T\)
\item Partitioning the subsets of the data passed to the left child and right child.  All elements of \(X'\) are passed to the left child with a weight of \(w_i \times p_H\) and to the right child with a weight of \(w_i \times p_T\).  See Figure~\ref{fig_unknown_values}.
\ee

In terms of the testing process, the change in evaluation is as follows.
Hitherto, a test instance given to a decision tree ends up at precisely one leaf
node. The proportion of heads and tails at that leaf is used to make a
prediction, the simplest being that \(p_H = \frac{n_H}{n_H+n_T}\). Now, if
decision requires knowing the value of a feature that is unknown, we send the
instance down to both left and right childs and combine the resulting decision,
weighted by the proportions of heads and tails at that node. In
Figure~\ref{fig_unknown_accuracy}, we show how the accuracy of classification
falls off as the proportion of unknown values in the data set increases. We
change the proportion of unknown values by randomly selecting instances and
randomly selecting features to artificially turn to null values. The results are
reported for the data sets of Table~\ref{tbl_data_sets}.

\begin{figure}
\centering
\fbox{
\begin{minipage}{35cm}
TODO
\end{minipage}
}
\label{fig_unknown_values}
\caption{Change in weight of datum when split involves unknown values}
\end{figure}


\documentclass[]{article}
\usepackage{caption,subcaption,graphicx,float,url,amsmath,amssymb,tocloft,wasysym,amsthm,thmtools,textcomp,listings,amsfonts,cancel}
\usepackage[hidelinks]{hyperref}
\usepackage[toc,acronym,nonumberlist]{glossaries}
\usepackage[]{algorithm2e}
\setacronymstyle{long-short}
\usepackage{glossaries-extra}
\graphicspath{{figs/}}
\setlength{\cftsubsecindent}{0em}
\setlength{\cftsecnumwidth}{3em}
\setlength{\cftsubsecnumwidth}{3em}
\newcommand\numberthis{\addtocounter{equation}{1}\tag{\theequation}}
\newtheorem{thm}{Theorem}
\newtheorem{cor}[thm]{Corollary}
\setcounter{tocdepth}{1}
\usepackage[toc,page]{appendix}
%opening
\title{
	Computation in Complex Systems\\
	Week 2\\
	 Algorithms \& Landscapes
}

%\makeglossaries

\begin{document}

\maketitle

\tableofcontents
%\listofalgorithms
\section{Maximum independent set}

\subsection{What is the maximum value of the independent set?}

\begin{figure}[H]
	\begin{center}
		\caption{What is the maximum value of the independent set?}
		\includegraphics[width=0.6\textwidth]{mwisQ}
	\end{center}
\end{figure}

Observations:
\begin{enumerate}
	\item A tree with $N$ nodes has $2^N-1$ non empty subsets, so a brute-force search is exponential. Let's not go there!
	\item Either the maximal independent set includes the root of the tree, or it doesn't.
	\item If the the maximal independent set doesn't include the root of the tree, each subtree must be maximal.
	\item If the maximal independent set includes the root of the tree, each subtree must have a set that excludes the node.
\end{enumerate}

So we can solve this problem as follows:
\begin{enumerate}
	\item Either the root node, $3$ is in $M$ or it isn't.
	\item If $3$ is in the tree, we have to exclude {4,1,5}, which gives a score of $3+(1+2)+2+(1+1)=9$
	\item If $3$ isn't in the tree, the maximum is the sum of the maxima for the 4 subtrees:
	\begin{enumerate}
		\item (4,(1,2)): since $1+2<4$, the maximum value is 4.
		\item (1,(2)): since $1<2$, the maximum value is 2.
		\item ((5),(1,1)): since $1+1<5$, the maximum value is 5.
	\end{enumerate}
	\item Putting this together, if $3$ isn't in the tree, the maximum is $4+2+5=10$
\end{enumerate}

\subsection{Algorithm for maximum independent set}

Algorithm \ref{alg:find:maximum:independent:set} is a description is pseudo-code, and Appendix \ref{sect:ref:implementation} provides a reference implementation in Python.

\begin{algorithm}[H]
	\KwData{A binary tree $T$ with a value $V(n)$ assigned to each node $n$}
	\KwResult{A set $S\subseteq N(T) | I(S) \land \sum_{s\in S} V(s) \ge \sum_{s\in S^\prime} V(s) \forall S^\prime \subset N(T) \text{ with } I(S^\prime)$  }
	initialization\;
	Starting at the root of the tree, traverse it top down to build two data structures: a stack \emph{unprocessed}, which has the \emph{root} at the bottom, and the $leaves$ at the top; and a dictionary \emph{score}, which is initially empty, but will map each node to the value of the maximum independent set starting at that node.\;
	\While {$length(unprocessed)>0$}{
		$node \leftarrow pop(unprocessed)$\;
		\eIf {leaf(node)}{
			$score(node)\leftarrow value(node)$ \;}{
			$val_{out} \leftarrow \sum_{\forall child(node)} score(child)$\;
			$val_{in} \leftarrow value(node) + \sum_{\forall grandchild(node)}  value(grandchild)$\;
			$score(node)\leftarrow max(val_{out},val_{in})$\;
		}
	}
	\caption{Find the maximum independent set from a tree}\label{alg:find:maximum:independent:set}
\end{algorithm}

\section{Reductions \& Translations}

Table \ref{table:transform} illustrates the transformation of ASTRO to START. It exploits the common subsequence S-T-R to produce a minimum set of transformations.
\begin{table}[H]
	\begin{center}
		\caption{Transforming ASTRO to START}\label{table:transform}
		\begin{tabular}{|c|c|c|c|c|c|c|l|l|} \hline
			\textbf{\cancel{A}}&S&T&&R&O&&delete&$\rightarrow$\\\hline
			&\textbf{S}&T&&R&O&&match&$\searrow$\\\hline
			&S&\textbf{T}&&R&O&&match&$\searrow$\\\hline
			&S&T&\textbf{A}&R&O&&insert&$\downarrow$\\\hline
			&S&T&A&\textbf{R}&O&&match&$\searrow$\\\hline
			&S&T&A&R&\textbf{\cancel{O}}&&delete&$\rightarrow$\\\hline
			&S&T&A&R&&\textbf{T}&insert&$\downarrow$\\\hline
		\end{tabular}
	\end{center}
\end{table}

\subsection{Assign Weights}

Figure \ref{fig:Shortest:path} shows the path that gives rise to the transformations in Table \ref{table:transform}. In order to make this the shortest path we need the following rules:
\begin{itemize}
	\item If two symbols are equal, accept them and mover diagonally;
	\item If two symbols don't match, insert or delete; disfavour mismatches (point mutations).
\end{itemize}

The simplest way to achieve this is with the weights shown in Figure \ref{fig:weights}.
\begin{figure}[H]
	\begin{center}
		\caption{Weights}\label{fig:weights}
		\includegraphics[width=\textwidth]{q2-weights}
	\end{center}
\end{figure}

\subsection{Shortest path}

Copying the weights from Figure \ref{fig:weights} we get Figure \ref{fig:Shortest:path}. The shorted path has length $1+0+0+1+0+1+1=4$.
\begin{figure}[H]
	\begin{center}
		\caption{Shortest path}\label{fig:Shortest:path}
		\includegraphics[width=0.6\textwidth]{q2-path}
	\end{center}
\end{figure}

% glossary
%\printglossaries

% bibliography go here

%\bibliographystyle{unsrt}
%\addcontentsline{toc}{section}{Bibliography}
%\bibliography{origins,wikipedia}

\begin{appendices}
	\section{Reference Implementation for Algorithm \ref{alg:find:maximum:independent:set}}\label{sect:ref:implementation}
	One way to implement Algorithm \ref{alg:find:maximum:independent:set} is with the following code.
	\lstinputlisting[language=Python]{mis.py}
\end{appendices}

\end{document}


\providecommand{\main}{../..}
\documentclass[\main/thesis.tex]{subfiles}
\begin{document}

\section{Systems with an Upper Bound}\label{bounded}

We say that a system is \textit{bounded} if there \textit{exists} a maximum in
such system. In Agda, an existential proposition like this is expressed with
a \textit{dependent sum type}, which is essentially a \textit{pair} where the
type of the second term is dependent on the first.

\begin{lstlisting}
data Σ (A : Set) (B : A → Set) : Set where
    _,_ : (x : A) → B x → Σ A B

proj₁ : ∀ {A B} → Σ A B → A
proj₂ : ∀ {A B} → (pair : Σ A B) → B (proj₁ pair)
\end{lstlisting}

For example, to prove that there exists a right identity for addition on natural numbers;
we place the ``evidence'' on the left and its justification on the right.

\begin{lstlisting}
prop : ∀ n → Σ ℕ (λ e → n + e ≡ n)
prop n = 0 , +-right-identity n
\end{lstlisting}

With a little syntax sugar, we can rewrite the proposition as
{\lstinline|Σ[ x ∈ A ] B|} instead of {\lstinline|Σ A (λ x → B)|}.

\begin{lstlisting}
Bounded : ∀ b d o → Set
Bounded b d o = Σ[ xs ∈ Numeral b d o ] Maximum xs
\end{lstlisting}

The proposition above can be read as follows:
\textit{There exists a numeral {\lstinline|xs|} of type
{\lstinline|Numeral b d o|} such that {\lstinline|Maximum xs|} holds.}
To prove that a system is bounded, we have to place the numeral that we consider
to be a maximum on the left of the pair, and a proof to justify it on the right.

\subsection{Properties of each Category}

\paragraph{NullBase}

We have proven that any numeral with the greatest digit as its LSD is a maximum.

\begin{lstlisting}
Bounded-NullBase : ∀ d o → Bounded 0 (suc d) o
Bounded-NullBase d o =
    (greatest-digit d ∙) ,
    (Maximum-NullBase-Greatest
        (greatest-digit d ∙)
        (greatest-digit-is-the-Greatest d))
\end{lstlisting}

\paragraph{NoDigits}

Since {\lstinline|¬ (Bounded b 0 o)|} reduces to {\lstinline|Bounded b 0 o → ⊥|},
we are given an addition argument, which is a proof claiming that the system is
bounded.
Pattern match on this argument yields a pair with a numeral of
{\lstinline|Numeral b 0 o|} on the left.
At this point, we do not actually care whether the numeral is a maximum
because it should not have existed in the first place.
Hand the numeral to {\lstinline|NoDigits-explode|} and we are done.

\begin{lstlisting}
Bounded-NoDigits : ∀ b o → ¬ (Bounded b 0 o)
Bounded-NoDigits b o (xs , claim) = NoDigits-explode xs
\end{lstlisting}

\paragraph{AllZeros}

Similar to that of {\lstinline|Bounded-NullBase|}:

\begin{lstlisting}
Bounded-AllZeros : ∀ b → Bounded (suc b) 1 0
Bounded-AllZeros b = (z ∙) , Maximum-AllZeros (z ∙)
\end{lstlisting}

\paragraph{Proper}

This proposition is proven by contradicting the fact that
a proper numeral system has no maximum.

\begin{lstlisting}
Bounded-Proper : ∀ b d o → (proper : 2 ≤ suc (d + o))
    → ¬ (Bounded (suc b) (suc d) o)
Bounded-Proper b d o proper (xs , claim) =
    contradiction claim (Maximum-Proper xs proper)
\end{lstlisting}

\subsection{The Decidable Predicate}

We can determine whether a system is bounded by delegating the job to the
helper functions we have defined above.

\begin{lstlisting}
Bounded? : ∀ b d o → Dec (Bounded b d o)
Bounded? b d o with numView b d o
Bounded? _ _ _ | NullBase d o
    = yes (Bounded-NullBase d o)
Bounded? _ _ _ | NoDigits b o
    = no (Bounded-NoDigits b o)
Bounded? _ _ _ | AllZeros b
    = yes (Bounded-AllZeros b)
Bounded? _ _ _ | Proper b d o proper
    = no (Bounded-Proper b d o proper)
\end{lstlisting}

\paragraph{Summary}

\begin{center}
    \begin{adjustbox}{max width=\textwidth}
    \begin{tabular}{ | l | c | c | c | c | }
    \textbf{Properties} & \textbf{NullBase} & \textbf{NoDigits} & \textbf{AllZeros} & \textbf{Proper} \\
    \hline
    has an maximum     & yes & no & yes & no \\
    has an upper bound & yes & no & yes & no \\
    \end{tabular}
    \end{adjustbox}
\end{center}

\end{document}


% This LaTeX was auto-generated from MATLAB code.
% To make changes, update the MATLAB code and republish this document.

\documentclass{article}
\usepackage{graphicx}
\usepackage{color}

\sloppy
\definecolor{lightgray}{gray}{0.5}
\setlength{\parindent}{0pt}

 \input{preamble} \begin{document} \maketitle \tableofcontents

{}

\section{Problem 20.2-1}

\begin{par}

Solute A is diffusing at unsteady state into a semi-infinite medium of
pure B and undergoes a first-order reaction with B. Solute A is
dilute. Calculate the concentration $c_A$ at points z = 0, 4, and
$10~\textup{mm}$ from the surface for $t=1\times 10^5~\textup{s}$.
Physical property data are $D_{AB}=1\times
10^{-9}~\textup{m}^2/\textup{s}$, $k'=1\times 10^{-4}~\textup{s}^{-1}$,
$c_{A0} = 1.0~\textup{kg mol/m}^3$. Also calculate the
$\textup{kg mol absorbed/m}^2$.
The concentration of A at point z is given by \vref{conc_empirical_1}
(corrected version),
\begin{equation} \label{conc_empirical_1}
\begin{split}
\frac{C_A}{C_{A0}}=&\frac{1}{2}\exp\left(-z\sqrt{k'/D_{AB}}\right)
 * \textup{erfc}\left(\frac{z}{2\sqrt{tD_{AB}}}-\sqrt{k't}\right) \\
 &+ \frac{1}{2}\exp\left(z\sqrt{k'/D_{AB}}\right)
 * \textup{erfc}\left(\frac{z}{2\sqrt{tD_{AB}}}+\sqrt{k't}\right)
\end{split}
\end{equation}
\Vref{conc_empirical_1} was incorrect in the textbook, and has
been modified according to the instructor's email.

\end{par} \vspace{1em}
\begin{verbatim}
u = symunit;
t = 1e5 * u.s;
D_AB = 1e-9 * u.m^2 / u.s;
k_prime = 1e-4 * u.s^-1;
C_A0 = 1.0 * (u.kg * u.mol) / (u.m^3);
C_A = @(z) C_A0 * ...
    (0.5*exp(-z*sqrt(k_prime/D_AB))*erfc((z/(2*sqrt(t*D_AB))) - sqrt(k_prime*t)) + ...
    0.5*(exp(z*sqrt(k_prime/D_AB)))*erfc((z/(2*sqrt(t*D_AB))) + sqrt(k_prime*t)));
disp(unitString(C_A(0), 'C_A (0 mm)'))
disp(unitString(C_A(0.004 * u.m), 'C_A (4 mm)'))
disp(unitString(C_A(0.010 * u.m), 'C_A (10 mm)'))
\end{verbatim}

        \color{lightgray} \begin{verbatim}C_A (0 mm): 1 (kg*mol)/m^3
C_A (4 mm): 0.28226 (kg*mol)/m^3
C_A (10 mm): 0.042328 (kg*mol)/m^3
\end{verbatim} \color{black}
    \begin{par}

The amount of A absorbed per square meter is given by
\vref{conc_empirical_2} (corrected version),
\begin{equation} \label{conc_empirical_2}
Q=C_{A0}\sqrt{D_{AB}/k'}\left[\left(k't+\frac{1}{2}\right)\textup{erf}\sqrt{k't}+\sqrt{k't/\pi} e^{-k't}\right]
\end{equation}

\end{par} \vspace{1em}
\begin{verbatim}
Q = C_A0*sqrt(D_AB/k_prime)*((k_prime*t+0.5)*erf(sqrt(k_prime*t) + ...
    sqrt((k_prime*t)/pi)*exp(-k_prime*t)));
disp(unitString(Q))
\end{verbatim}

        \color{lightgray} \begin{verbatim}Q: 0.033204 (kg*mol)/m^2
\end{verbatim} \color{black}

\section{Problem 21.1-2}

\begin{par}
Prove or show the following relationships, starting with the flux equations:
\end{par} \vspace{1em}
\begin{par}

\subsection{Part a}
Convert $k_c'$ to $k_y$ and $k_G$.

\end{par} \vspace{1em}
\begin{par}
The flux equations involving these coefficients are, $$ N_A = k_c(C_{A1}-C_{A2}) = k_G(p_{A1}-p_{A2}) = k_y(y_{A1}-y_{A2}) $$ the conversion between $k_c'$ and $k_c$ is given in the flux equation for A diffusing through stagnant, non-diffusing B,
\end{par} \vspace{1em}
\begin{par}

\[N_A = \frac{k_c'}{x_{BM}}\left(C_{A1}-C_{A2}\right) =
k_c\left(C_{A1}-C_{A2}\right)\]
\[ k_c = \frac{k_c'}{x_{BM}} = \frac{k_c' P}{p_{BM}} \]
\[ k_y = \frac{k_c' P}{p_{BM}}\cdot\frac{C_{A1}-C_{A2}}{y_{A1}-y_{A2}} = \frac{k_c' P}{RT\cdot y_{BM}} \]
\[ k_G = \frac{k_c' P}{p_{BM}}\cdot\frac{C_{A1}-C_{A2}}{p_{A1}-p_{A2}} = \frac{k_c' P}{RT\cdot p_{BM}} \]

\end{par} \vspace{1em}
\begin{par}

\subsection{Part b}
Convert $k_L$ to $k_x$ and $k_x'$.

\end{par} \vspace{1em}
\begin{par}
The flux equations involving these coefficients are, $$ N_A = k_L(c_{A1}-c_{A2}) = k_x(x_{A1}-x_{A2}) $$ A relationship for $k_x$ can be found by substituting $x=c_{A}/c$, $$ k_x = k_L\frac{c_{A1}-c_{A2}}{x_{A1}-x_{A2}} = k_L\frac{c_{A1}-c_{A2}}{\frac{c_{A1}}{c}-\frac{c_{A2}}{c}} = k_L\cdot c $$ $$ k_x' = k_x\cdot x_{BM} = k_L \cdot c \cdot x_{BM} $$
\end{par} \vspace{1em}
\begin{par}

\subsection{Part c}
Convert $k_G$ to $k_y$ and $k_c$.

\end{par} \vspace{1em}
\begin{par}
The flux equations involving these coefficients are, $$ N_A = k_G(p_{A1}-p_{A2}) = k_y(y_{A1}-y_{A2}) = k_c(C_{A1}-C_{A2}) $$ By substituting $p_A=y_A P$, $$ k_y = k_G\frac{p_{A1}-p_{A2}}{y_{A1}-y_{A2}} = k_G\frac{y_{A1}P-y_{A2}P}{y_{A1}-y_{A2}} = k_G\cdot P $$ Finally, substituting $C=P/RT$, $$ k_c = k_G\frac{p_{A1}-p_{A2}}{C_{A1}-C_{A2}} = k_G\frac{p_{A1}-p_{A2}}{(p_{A1}-p_{A2})/RT} = k_G \cdot RT $$
\end{par} \vspace{1em}

\section{Problem 21.1-3}

\begin{par}
In a wetted-wall tower an air- $\textup{H}_2\textup{S}$ mixture is flowing by a film of water that is flowing as a thin film down a vertical plate. The $\textup{H}_2\textup{S}$ is being absorbed from the air to the water at a total pressure of $1.50~\textup{atm abs}$ and $30~\textup{C}$. A value for $k_c'$ of $9.567\times 10^{-4}~\textup{m/s}$ has been predicted for the gas-phase mass-transfer coefficient. At a given point, the mole fraction of $\textup{H}_2\textup{S}$ in the liquid at the liquid-gas interface is $2.0(10^{-5})$ and $p_A$ of $\textup{H}_2\textup{S}$ in the gas is $0.05~\textup{atm}$. The Henry's law equilibrium relation is $p_A(\textup{atm}) = 609x_A$ (mole fraction in liquid). Calculate the rate of absorption of $\textup{H}_2\textup{S}$. (\textit{Hint}: Call point 1 the interface and point 2 the gas phase. Then, calculate $p_{A1}$ from Henry's law and the given $x_A$. The value of $p_{A2}$ is $0.05~\textup{atm}$.)
\end{par} \vspace{1em}
\begin{par}
The mass transfer coefficient given is $k_c'$, which can be converted to $k_G$ for a flux equation based on partial pressures. $$ k_G = \frac{k_c' P}{RT\cdot p_{BM}} $$ $$ N_A = k_G(p_{A1} - p_{A2}) $$
\end{par} \vspace{1em}
\begin{verbatim}
u = symunit;
P = 1.5 * u.atm;
T = rewrite(30 * u.Celsius, u.K, 'Temperature', 'absolute');
k_c_prime = 9.567e-4 * u.m / u.s;
x_A1 = 2.0e-5;
p_A1 = 609 * x_A1 * u.atm;
p_A2 = 0.05 * u.atm;
p1 = separateUnits(p_A1); p2 = separateUnits(p_A2); p = separateUnits(P);
p_BM = ((p-p1)-(p-p2))/log((p-p1)/(p-p2)) * u.atm;
R = 8.2057338e-5 * (u.m^3 * u.atm) / (u.mol * u.K);
k_G = (k_c_prime * P) / (R * T * p_BM);
N_A = k_G * (p_A1 - p_A2) * 1e-3 * u.kg;  % convert to kg-mol
disp(unitString(N_A))
\end{verbatim}

        \color{lightgray} \begin{verbatim}N_A: -1.4854e-06 (kg*mol)/(m^2*s)
\end{verbatim} \color{black}

\section{Problem 21.2-1}

\begin{par}
A fluid is flowing in a vertical pipe and mass tranfer is occurring from the pipe wall to the fluid. Relate the convective mass-transfer coefficient $k_c'$ to the variables $D$, $\rho$, $\mu$, $v$, $D_{AB}$, $g$, and $\Delta{\rho}$, where $D$ is pipe diameter, $L$ is pipe length, and $\Delta{\rho}$ is the density difference.
\end{par} \vspace{1em}
\begin{par}
According to Buckingham's pi theorem, given nine independent variables in three physical dimensions, there are six $\pi-$ groups to construct.
\end{par} \vspace{1em}
\begin{par}
Vector order: [Length Mass time]
\end{par} \vspace{1em}
\begin{verbatim}
D = [1 0 0];
L = [1 0 0];
rho = [-3 1 0];
mu = [-1 1 -1];
v = [1 0 -1];
D_AB = [2 0 -1];
g = [1 0 -2];
delta_rho = [-3 1 0];
k_c_prime = [1 0 -1];

pi_1 = ([D' rho' mu']\-k_c_prime')'
\end{verbatim}

        \color{lightgray} \begin{verbatim}
pi_1 =

     1     1    -1

\end{verbatim} \color{black}
    \begin{par}
$\pi_1 = \frac{D \rho k_c'}{\mu}$
\end{par} \vspace{1em}
\begin{verbatim}
pi_2 = ([D' rho' mu']\-v')'
\end{verbatim}

        \color{lightgray} \begin{verbatim}
pi_2 =

     1     1    -1

\end{verbatim} \color{black}
    \begin{par}
$\pi_2 = \frac{D \rho v}{\mu}$
\end{par} \vspace{1em}
\begin{verbatim}
pi_3 = ([D' rho' mu']\-D_AB')'
\end{verbatim}

        \color{lightgray} \begin{verbatim}
pi_3 =

     0     1    -1

\end{verbatim} \color{black}
    \begin{par}
$\pi_3 = \frac{\rho D_{AB}}{\mu}$
\end{par} \vspace{1em}
\begin{verbatim}
pi_4 = ([D' rho' mu']\-g')'
\end{verbatim}

        \color{lightgray} \begin{verbatim}
pi_4 =

     3     2    -2

\end{verbatim} \color{black}
    \begin{par}
$\pi_4 = \frac{D^3 \rho^2 g}{\mu^2}$
\end{par} \vspace{1em}
\begin{verbatim}
pi_5 = ([D' rho' mu']\-delta_rho')'
\end{verbatim}

        \color{lightgray} \begin{verbatim}
pi_5 =

     0    -1     0

\end{verbatim} \color{black}
    \begin{par}
$\pi_5 = \frac{\Delta\rho}{\rho}$
\end{par} \vspace{1em}
\begin{verbatim}
pi_6 = ([D' rho' mu']\-L')'
\end{verbatim}

        \color{lightgray} \begin{verbatim}
pi_6 =

    -1     0     0

\end{verbatim} \color{black}
    \begin{par}
$\pi_6 = \frac{L}{D}$
\end{par} \vspace{1em}
\begin{par}
These dimensionless groups can be combined to describe the system. This combination was done pencil-and-paper with a lot of frustration, and the results are shown here.
\end{par} \vspace{1em}
\begin{par}

\[ \frac{\pi_1}{\pi_3} = f\left(\pi_4 \pi_5 \pi_6^3, \pi_2,
\pi_3^{-1}\right) \]
\[ \frac{k_c'D}{D_{AB}}=f\left(\frac{gL^3\rho\Delta\rho}{\mu^2},
\frac{Dv\rho}{\mu},\frac{\mu}{\rho D_{AB}}\right) \]

\end{par} \vspace{1em}

\section{Problem 21.3-1}

\begin{par}
Using the data and physical properties of Example 21.3-2, calculate the flux for a water velocity of $0.152~\textup{m/s}$ and a plate length of $L = 0.137~\textup{m}$. Do not assume that $x_{BM}=1.0$ but actually calculate its value.
\end{par} \vspace{1em}
\begin{par}
The Schmidt and Reynolds numbers can be calculated from the given quantities,
\end{par} \vspace{1em}
\begin{verbatim}
u = symunit;
T = rewrite(26.1 * u.Celsius, u.K, 'Temperature', 'absolute');
L = 0.137 * u.m;
v = 0.152 * u.m / u.s;
solubility = 0.02948 * (u.kg * u.mol) / (u.m^3);
D_AB = 1.245e-9 * u.m^2 / u.s;
mu = 8.71e-4 * u.Pa * u.s;
rho = 996 * u.kg / u.m^3;
N_Sc = simplify(mu / (rho * D_AB));
N_Re = simplify((L * v * rho)/mu);
disp(unitString(N_Sc))
disp(unitString(N_Re))
\end{verbatim}

        \color{lightgray} \begin{verbatim}N_Sc: 702.408 1
N_Re: 23812.5189 1
\end{verbatim} \color{black}
    \begin{par}
This value for the Reynolds number corresponds to this equation for mass flux, $$ J_D = 0.99N_{Re,L}^{-0.5} = \frac{k_c'}{v}\left(N_{Sc}\right)^{2/3} $$
\end{par} \vspace{1em}
\begin{verbatim}
syms k_c_prime;
k_c_prime = solve(0.99*N_Re^-0.5 == (k_c_prime/v)*(N_Sc^(2/3)));
FW_water = 18.02* u.kg / (u.kg * u.mol);
c = rho / FW_water;
x_A1 = 0;
x_A2 = solubility / (solubility + c);
x_BM = (1 - (1 - x_A2))/log(1/(1 - x_A2));
k_c = k_c_prime / x_BM;
N_A = k_c * solubility;
disp(unitString(N_A))
\end{verbatim}

        \color{lightgray} \begin{verbatim}N_A: 3.6391e-07 (kg*mol)/(m^2*s)
\end{verbatim} \color{black}

 \section{Referenced Functions} \subsection{unitString.m} \begin{verbatim} function displayString = unitString(quantity, name)
%UNITSTRING Display a 1x2 sym with symbolic units
% USAGE: unitString(some_quantity, name)
% OUTPUT:
%   - displayString: char vector containing name, scalar, and units
if nargin < 2
    n = inputname(1);
else
    n = name;
end

[s, U] = separateUnits(quantity);
formatSpec = '%s: %s %s';
displayString = sprintf(formatSpec, n, num2str(double(s)), symunit2str(U));
end \end{verbatim} \end{document}