\section{Levitating the Universe of Descriptions}
\label{sec:desc-levitate}

In this section, we will fulfil our promises and show how we implement
the signatures, first for the enumerations, and then for the codes of
the $\Desc^n$ universe.  Persuading these programs to perform was a perilous
pedagogical peregrination for the protagonist.  Our method was indeed to
hardwire constants implementing the signatures specified above, in the
first instance, but then attempt to replace them, step by step, with
\emph{definitions}: ``Is \(2+2\) still \(4\)?'', ``No, it's a loop!''.
But we did find a way, so now we hope to convey to you the
dizzy feeling of levitation, without the falling.

\subsection{Implementing finite enumerations}

\begin{wstructure}
<- Recall typing rules of 1st section
    -> Make clear they were just promises
    -> Can be implemented now
        <- Simply List UId
\end{wstructure}

In Section~\ref{sec:finite-sets}, we specified the
finite sets of tags. We are going to implement (at every universe level)
the $\EnumU$ type former and its constructors. Recall:
%
\[\Type{\EnumU}^n\qquad\Bhab{\NilE}{\EnumU}\qquad
\Bhab{\ConsE{(\Bhab{\M{t}}{\UId})}{(\Bhab{\M{E}}{\EnumU})}}{\EnumU}
\]
%
The $\NilE$ and $\SYMBConsE$ constructors are just the `nil' and
`cons' or ordinary lists, with elements from \(\UId\).
Therefore, we
implement:
%
\[
\EnumU \mapsto \Mu{(\ListD\: \UId)}
\qquad
\NilE \mapsto \ListNil
\qquad
\ConsE{\M{t}}{\M{E}} \mapsto \ListCons{\M{t}}{\M{E}}
\]

\begin{wstructure}
<- Consequences
    -> Type theory doesn't need to be extended with EnumU, NilE, and ConsE
        <- EnumU == Mu EnumUD
        <- NilE, ConsE are just tags
    -> Do not need a specific \spi eliminator
        <- \spi is an instance of the generic eliminator
            <- Code?
    -> Anything else remains the same (switch, EnumT, 0, 1+)
\end{wstructure}

Let us consider the consequences. We find that the type
theory does not need a special type former $\EnumU$,
or special constructors $\NilE$ and $\SYMBConsE$.
Moreover, the $\spi{\M{E}}{\M{P}}$ operator, computing tuple types
of \(\M{P}\)s by recursion on \(\M{E}\) need not be hardwired: we can
just use the generic $\F{ind}$ operator, as we would for any ordinary
program.

Note, however, that the universe decoder \(\EnumT{\M{E}}\) \emph{is}
hardwired, as are the primitive \(\Ze\) and \(\Su\) that
we use for low-level values, and indeed the \(\SYMBswitch\) operator.
We cannot dispose of data altogether! We have, however, gained
the ordinariness of the enumeration \emph{codes}, and hence of generic
programs which manipulate them. Our next step is similar: we are going to
condense the entire naming scheme of datatypes \emph{into itself}.

%% Apart from these changes, we are left with implementing the other
%% components of finite sets. This consists of $\EnumT$, $\Ze$, $\Su$, as
%% well as the $\F{switch}$ operator. Note that the actual implementation
%% of $\EnumU$ does not influence our implementation of $\EnumT$: be they
%% hard-coded or codes in the $\Desc$ universe, the $\EnumU$ objects
%% behave the same. We have witnessed this effect when carrying the
%% operation in Epigram, moving from an hard-coded presentation to a
%% self-hosted one. Absolutely no change to the $\EnumT$ objects was
%% required.

\begin{wstructure}
<- Summary of the operation
    <- The content of the type theory is exactly the same
        <- before == after
    /> type naming scheme condenses
        <- Replace named constructors by codes in the universe of datatypes
    -> Our next step is a similar move (in essence)
        /> Condenses the entire naming scheme of datatypes
\end{wstructure}

%In this section, we have replaced a low-level presentation of finite
%sets by a self-hosted one, expressed in the universe of
%descriptions. However, formally, the content of the type theory
%remains unchanged: objects that were present before the modification
%are still there. Conversely, we have not introduced any spurious
%object.

%If not on the content, this modification had an effect on the
%names. The type naming scheme of the type theory has condensed: named
%type formers ($\EnumU$) and constructors ($\NilE$ and $\ConsE$) are
%now replaced by codes and their fixpoint in the universe of
%descriptions. In essence,

\subsection{Implementing descriptions}

\begin{wstructure}
<- Realising our promises
    <- We are going to implement Desc
    /> Desc is itself a datatype
        <- Same situation as EnumU
            <- We want to benefit from generic operations
        -> It ought to be encoded in itself
\end{wstructure}

The set of codes, $\Desc$, is already some sort of datatype; as with $\EnumU$,
we ought to be able to describe it, coding of $\Desc^n$ in $\Desc^{n+1}$, spiralling
upwards. Hence, this code would be a first-class citizen, born with
the generic equipment of datatypes.

\subsubsection{First attempt}

\begin{wstructure}
<- A partial implementation
    <- '1 and 'indx are easy
    <- 'sigma is partially doable
        /> lack the ability to do an higher-order inductive call
    -> Show partial code [figure]
\end{wstructure}

Our first attempt gets stuck quite quickly:
%
\[\stk{
\DescD^n : \Desc^{n+1} \\
\DescD^n \mapsto
  \tada{\stkm{\DUnit\\ \SYMBDSigma\\ \SYMBDIndx}}
       {\stkm{ \DUnit                                \\
         \DSigma{\Set^n}{\LAM{\V{S}} \SHED}      \\
         \DIndx{\DUnit}    }                    } \\
%\begin{array}{@{}ll}
%\DescD \mapsto \DSigma{\!}{\!} & (\EnumT{[ \DUnit, \DSigma{\!}{\!}, \DIndx{\!} ]})  \\
%                               & \left[\begin{array}{l}
%                                   \DUnit                                \\
%                                   \DSigma{\Set}{\LAM{\V{S}} \SHED}      \\
%                                   \DIndx{\DUnit}                        \\
%                                 \end{array}
%                                 \right]
%\end{array}
}\]
%
Let us explain where we stand. Much as
we have done so far, we first offer a constructor choice from
$\DUnit$, $\SYMBDSigma$, and $\SYMBDIndx$. You may notice
that the `tagged' notation we have used for the $\Desc^n$ constructors now
fits the facts: these were actually the tags we are defining.
For $\DUnit$, we immediately reach the end of the description. For
$\SYMBDIndx$, there is a single recursive argument. Describing
$\SYMBDSigma$ is problematic. Recall the specification of
$\SYMBDSigma$:
%
\[
\DSigma{(\Bhab{\V{S}}{\Set^n})}{(\Bhab{\V{D}}{\V{S} \To \Desc^n})} : \Desc^n\\
\]
%
So, we first pack a  $\Set^n$, $\V{S}$, as well we might when working in
$\Desc^{n+1}$. We should then like
a recursive argument \emph{indexed} by $\V{S}$, but
that is an \emph{exponential}, and our presentation so far delivers only
sums-of-products. To code our universe, we must first enlarge it!

\subsubsection{Second attempt}

\begin{wstructure}
<- Extending the universe of description
    -> With higher-order induction
    <- Intuition: index elements in X by H, and go on reading
        -> indx is isomorph to hindx for H = 1
    /> Keep indx
        <- First order!
        -> Extensionally equal to hindx 1
        /> Practically, definitional equality on Sigma/Pi cannot cope with it
    -> Show DescD code
\end{wstructure}

In order to capture a notion of higher-order induction, we add a code
$\SYMBDHindx$ that takes an indexing set $\V{H}$. This amounts to give
a recursive subobject for each element of $H$.
%
\[\stk{
    \DHindx{(\Bhab{\M{H}}{\Set^n})}{(\Bhab{\M{D}}{\Desc^n})} : \Desc^n
    \smallskip \\
    \descop{\DHindx{\V{H}}{\V{D}}}{\V{X}}
        \mapsto
            \TIMES{(\V{H} \To \V{X})}{\descop{\V{D}}{\V{X}}} \\
}\]

Note that up to isomorphism, $\SYMBDIndx$ is subsumed by
$\DHindx{\Unit}{\!}$. However, the apparent duplication has some
value.  Unlike its counterpart, $\SYMBDIndx$ is first-order: we prefer
not to demand dummy functions from \(\Unit\) in ordinary data,
e.g. \(\NatSuc{(\LAM{\_}n)}\). It is na{\"\i}ve to imagine that up to
isomorphism, any representation of data will do.  First-order
representations are finitary by construction, and thus admit a richer,
componentwise decidable equality than functions may in
general possess.\footnote{E.g., extensionally, there is one function in
\(\EnumT{\void}\To\Nat\); intensionally, there is a countable infinitude
which it is dangerous to identify definitionally.}

We are now able to describe our universe of datatypes:
%
\[\stk{
\DescD^n : \Desc^{n+1} \\
\DescD^n \mapsto\tada
{\stkm{ \DUnit\\ \SYMBDSigma\\ \SYMBDIndx\\ \SYMBDHindx}}
{\stkm{
\DUnit                                            \\
                                   \DSigma{\Set^n}{\LAM{\V{S}} \DHindx{\V{S}}{\DUnit}}   \\
                                   \DIndx{\DUnit}                                    \\
                                   \DSigma{\Set^n}{\LAM{\_} \DIndx{\DUnit}}
}}
}\]
%
The $\DUnit$ and $\SYMBDIndx$ cases remain unchanged, as expected. We
successfully describe the $\SYMBDSigma$ case via
the higher-order induction, branching on $\V{S}$. The $\SYMBDHindx$ case
just packs a $\Set^n$ with a recursive argument.

At a first glance, we have achieved our goal. We have described the
codes of the universe of descriptions. The fixpoint of \(\descop{\DescD^n}\)
is a datatype just like $\Desc^n$, in $\Set^{n+1}$. Might we be
so bold as to take \(\Desc^n \mapsto \Mu{\DescD^n}\) as the levitating
definition? If we do, we shall come down with a bump!
To complete our levitation, just as in the magic trick, requires
hidden assistance. Let us explain the problem and reveal the `invisible
cable' which fixes it.

\subsubsection{Final move}

\begin{wstructure}
<- Subtlety: translation of [ ... ]
    -> Let us do it manually
        -> Code with problem for the motive of switch
\end{wstructure}

The definition \(\Desc^n \mapsto \Mu{\DescD^n}\) is circular,
but the offensive recursion is concealed by a prestidigitation.

Expanding \(\toDesc{-}\) and propagating types as in
Figure~\ref{fig:type-checking} reveals the
awful truth:
\[
\Desc^n\mapsto
\Mu{\stk{(\DSigma{\EnumT{\sqr{ \DUnit\:\: \SYMBDSigma\:\: \SYMBDIndx\:\: \SYMBDHindx}}\\}
    {\;\SYMBswitch\:\sqr{ \DUnit\:\: \SYMBDSigma\:\: \SYMBDIndx\:\: \SYMBDHindx}\:(\LAM{\_}\Desc^{n+1})\\
\;\sqr{\stkm{
\DUnit                                            \\
                                   \DSigma{\Set^n}{\LAM{\V{S}} \DHindx{\V{S}}{\DUnit}}   \\
                                   \DIndx{\DUnit}                                    \\
                                   \DSigma{\Set^n}{\LAM{\_} \DIndx{\DUnit}}
}}})}}
\]
The recursion shows up only because we must specify the return type
of the general-purpose \(\F{switch}\), and it is computing a \(\Desc^{n+1}\)!
Although type propagation
allows us to hide this detail \emph{when defining a function}, we cannot
readily suppress this information and check types
when \(\F{switch}\) is fully applied.

\begin{wstructure}
<- The magician trick
    <- Our problem is to give a motive for switch
        /> We perfectly know what it ought to be: \_ -> DescD
    -> Solution: extend the type theory with a special purpose switchD
        -> Only extension required to the type theory!
        -> Hidden away to the user by the syntactic sugar
            -> Sufficient to ensure unavailability as a raw operator
            <- Another instance of type propagation
\end{wstructure}

%What happens if we unfold the definition? We ought to build the following term:
%
%\[
%\PLAM{\V{x}}{(\EnumT{\V{E}})} \switch{\V{E}}{(\LAM{\_} %\Mu{\DescD})}{\V{\pi^f}}{\V{x}}
%\]
%
%But this is quite problematic. We are still in the process of
%constructing $\DescD$, and the motive of $\F{switch}$ is abruptly
%begging for this very same $\DescD$. Despite our willingness, we
%cannot materialise such motive. However, we perfectly know what the
%motive is.

We are too close to give up now. If only we did not need to
supply that return type, especially when we know what it must be!
We eliminate the recursion by \emph{specialising} \(\F{switch}\):
%
\[
\F{switchD} : \PITEL{\V{E}}{\EnumU}   \To
                (\spi{\V{E}}{\LAM{\_} \Desc^m}) \To
                \EnumT{\V{E}} \To \Desc^m
\]
%
The magician's art rests here, in this extension. We conceal it
behind a type propagation rule for \(\F{switchD}\) which we apply
with higher priority than for \(\F{switch}\) in general.
%
\[
\Rule{\Gamma \Vdash
  \propag{\push{\sqr{\vec{t}}}{\spi{\M{E}}{\PLAM{x}{\EnumT{E}}\Desc^m}}}
                           {\M{t'}}}
     {\Gamma \Vdash
\propag{\push{\sqr{\vec{t}}}{\EnumT{\M{E}}\To\Desc^m}}
 {\switchD{\M{E}}{\M{t'}}}}
\]
As a consequence, our definition above now propagates without
introducing recursion. Of course, by pasting together the declaration
of \(\Desc^n\) and its internal copy, we have made it appear in its own
type. Hardwired as a trusted \emph{fait accompli}, this creates no regress,
although one must assume the definition to recheck it.

Our Agda model does not formalise the $\SYMBswitchD$
construction. Instead, we exhibit the isomorphism between declared
and encoded descriptions. Here, $\SYMBswitchD$ lets us collapse
this isomorphism, operationally identifying defined and coded
descriptions.

There are other ways to achieve a sufficient specialisation to avoid a
recursive code, e.g., extending $\Desc^n$ with specialised codes for
\emph{finite} sums and products, pushing the $\SYMBswitch$ into the
interpretation of codes, rather than the code itself. Here, we prefer
not to add codes to $\Desc^n$ which are otherwise unmotivated.
%%
%\[\stk{
%    \Dsigma{(\Bhab{\V{E}}{\EnumU})}
%           {(\Bhab{\V{B}}{\spi{\V{E}}{\LAM{\_}{\Desc^n}}})} : \Desc^n
%    \smallskip \\
%    \descop{\Dsigma{\V{E}}{\V{B}}}{\V{X}}
%        \mapsto
%            \SIGMAS{\Bhab{\V{x}}{\EnumT{\V{E}}}}
%                   %{\descop{\switch{\V{E}}{(\LAM{\_}{\Desc})}{\V{B}}{\V{x}}}{\V{X}}}
%}\]
%%
%Using $\SYMBDsigma$ to switch over the $\Desc$ constructors, we avoid
%mention of $\Desc$ while levitating it. The $\SYMBswitch$ operator is
%pushed into the interpretation function, at which point $\Desc$ indeed
%exists.

%% * There exists alternative
%% **   <- All we want is to avoid mention of Desc
%% **   -> Add a code for finite sigma : $\sigma : (e : \EnumU) -> \pi e \Desc -> \Desc$
%% **       <- Take a finite set in argument
%% **       <- Interpreted as a switch
%% **       -> No more need to mention Desc during its construction
%% **           <- Pushed into intrepretation, at which time it exists

\begin{wstructure}
<- Generic programming now!
    <- Desc is just data
        -> Can be manipulated
    <- Free induction scheme on Desc
        -> Ability to inspect datatypes
        -> Ability to program on datatypes
\end{wstructure}

We have levitated \(\Desc\) at every level. Beyond its pedagogical
value, this exercise has several practical outcomes. First, it
confirms that each $\Desc$ universe is just plain data. As any piece of
data, it can therefore be inspected and manipulated. Moreover, it is
expressed in a $\Desc$ universe. As a consequence, it is equipped,
for free, with an induction principle. So, our ability to inspect and
program with $\Desc$ is not restricted to a meta-language: we have
the necessary equipment to program with \emph{data}, so we can program over
datatypes. \emph{Generic programming is just programming}.

\subsection{The generic catamorphism}

\begin{wstructure}
<- Making cata
    <- Present the type signature
    <- Starts with a call to generic induction
        <- induction on Desc!
        /> Show types at hand
        -> Explain how to use inductive hypothesis
    <- Implement the 'replace' function
    -> Dependent-typeless catamorphism
\end{wstructure}

In Section~\ref{sec:desc-fix-point}, we hardwired a dependent
$\F{ind}$uction principle, but sometimes,
iteration suffices. Let us construct the catamorphism.

\newcommand{\cata}{\F{cata}}

We proceed by induction on the \emph{data} in \(\Mu{\V{D}}\):
the non-dependent return type $\V{T}$ is readily propagated.
Given a node $\V{xs}$
and the induction hypotheses, the method ought to build an element of
$\V{T}$. Provided that we know how to make an element of
$\descop{\V{D}}{\V{T}}$, this step will be performed by the algebra
$\V{f}$. Let us take a look at this jigsaw:
%
\[\stk{
\cata : \PITEL{\V{D}}{\Desc}
           \PI{\V{T}}{\Set}
           (\descop{\V{D}}{\V{T}} \To \V{T}) \To
           \Mu{\V{D}} \To \V{T} \\
\cata\: \V{D}\: \V{T}\: \V{f} \mapsto
  \sind \LAM{\V{xs}}\LAM{\V{hs}} \V{f}\: \SHED
}\]
%
The hole remains: we have
\(\Bhab{\V{xs}}{\descop{\V{D}}{\Mu{\V{D}}}}\) and
\(\Bhab{\V{hs}}{\All{\V{D}}{\Mu{\V{D}}}{(\LAM{\_} \V{T})}{\V{xs}}}\)
to hand, and we need a \(\descop{\V{D}}{\V{T}}\).
Now, $\V{xs}$ has the right shape, but its
components have the wrong type. However, for each such
component, $\V{hs}$ holds the corresponding value
in $\V{T}$.  We need a function to \(\F{replace}\) the former with the latter:
this pattern
matching sketch yields an induction on \(\V{D}\). We fill the hole
with \(\F{replace}\: \V{D}\:(\Mu{\V{D}})\: \V{T}\: \V{xs}\: \V{hs}\).
%
\[\stk{
\F{replace} : \stk{\PITEL{\V{D}}{\Desc}
                   \PITEL{\V{X},\V{Y}}{\Set}\\
                   \PI{\V{xs}}{\descop{\V{D}}{\V{X}}}
                   \All{\V{D}}{\V{X}}{(\LAM{\_}\V{Y})}{\V{xs}} \To
                   \descop{\V{D}}{\V{Y}}} \\
\begin{array}{@{}l@{\:}l@{\:}l@{\:\:\mapsto\:\:}l}
\F{replace}\: \DUnit &          \V{X}\: \V{Y}\: \Void&          \Void   &
    \Void  \\
\F{replace}\: (\DSigma{\V{S}}{\V{D}})& \V{X}\: \V{Y}\: \pair{\V{s}}{\V{d}}{}& \V{d'}  &
    \pair{\V{s}}{\F{replace}\: (\V{D}\: \V{s})\: \V{X}\: \V{Y}\: \V{d}\: \V{d'}}{} \\
\F{replace}\: (\DIndx{\V{D}})&     \V{X}\: \V{Y}\: \pair{\V{x}}{\V{d}}{}& \pair{\V{y}}{\V{d'}}{} &
    \pair{\V{y}}{\F{replace}\: \V{D}\: \V{X}\: \V{Y}\: \V{d}\: \V{d'}}{} \\
\F{replace}\: (\DHindx{\V{H}}{\V{D}})& \V{X}\: \V{Y}\: \pair{\V{f}}{\V{d}}{}& \pair{\V{g}}{\V{d'}}{} &
 \pair{\V{g}}{\F{replace}\: \V{D}\: \V{X}\: \V{Y}\: \V{d}\: \V{d'}}{}
\end{array}
}\]
%

%% The astute reader will have been struck by the type of $\F{replace}$:
%% it is \emph{almost} the morphism part -- sometimes called \emph{map}
%% -- of the functor $\V{D}$ from $\V{X}$ to $\V{Y}$ in $\Set$. Just as
%% the $\F{induction}$ is the dependent version of $\cata$, $\F{replace}$
%% is the dependent version of the map, which uses the induction
%% hypotheses. For space reason, we will not present the non-dependent
%% map. It can be found in the Agda model.

\begin{wstructure}
<- Deriving generic functions
    <- Taking a Desc and computing a function
        <- Desc comes equipped with an induction principle
        -> Ability to compute more functions from it
            -> More generic functions
    <- Inspecting datatypes
        <- All described byu a Desc code
        -> Ability to explore the code
            <- Desc equipped with an induction principle
            -> Build new objects based on that structure
\end{wstructure}

We have shown how to derive a generic
operation, \(\cata\), from a pre-existing generic operation,
\(\F{ind}\), by
manipulating descriptions as data: the catamorphism is just a
function taking each $\Desc$ value to a datatype specific
operation. This is polytypic programming, as in
PolyP~\cite{jansson:polyp}, made ordinary.

%% Moreover, the $\F{replace}$ function demonstrates the benefit of an
%% approach based on universes. The datatypes living in the universe of
%% descriptions, we are able to \emph{inspect} them. As shown by
%% $\F{replace}$, it is easy to explore these structures, as well as
%% building new ones.

\subsection{The generic free monad}
\label{sec:desc-free-monad}

\begin{wstructure}
<- A generic program: the free monad construction
    <- Recall free monad construction in Haskell
        -> Based on a functor F
    <- Note that the free monad construction is itself defined by a functor
        -> Extract it
\end{wstructure}

In this section, we try a more ambitious generic operation. Given a
functor---a signature of operations represented as a tagged
description---we build its free monad, extending the signature with
variables and substitution.
%
\newcommand{\FMFreeMonad}{\D{FreeMonad}}
\newcommand{\FMFreeMonadD}{\D{FreeMonadD}}
\newcommand{\FMVar}{\C{Var}}
\newcommand{\FMComposite}{\C{Composite}}
%
Let us recall this construction in, say, Haskell. Given a
functor \texttt{f}, the free monad over \texttt{f} is given
thus:
%
\[
\texttt{data FreeMonad f x = Var x | Op (f (FreeMonad f x))}
\]
%
Provided \texttt{f} is an instance of \texttt{Functor}, we may
take \texttt{Var} for \texttt{return} and use \texttt{f}'s
\texttt{fmap} to define \texttt{>>=} as substitution.

Being an inductive type, $\FMFreeMonad$ arises
by a pattern functor:
%
\[
\FMFreeMonadD\: \V{F}\: \V{X}\: \V{Z} \mapsto \V{X} \mathop{\D{+}} \V{F}\:\V{Z}
\]

\begin{wstructure}
    <- Encode it in the Desc world [equation]
        <- F is the Desc we start with
        <- The free monad functor is what we have just defined
        <- [\_]* : Desc -> Set -> Desc
           [\_]* D X = 'cons ['var ('sigma X (\_ -> '1))] D
        -> Mu does the fixpoint
\end{wstructure}

Our construction takes the functor as a
tagged description, and given a set $\V{X}$ of variables, computes the
tagged description of the free monad pattern functor.
%
\[\stk{
\FreeMonad{\_} : \TagDesc \To \Set \To \TagDesc \\
\FreeMonad{\pair{\V{E}}{\V{D}}{}}\:\V{X} \mapsto
    \pair{\pair{\DVar{}}{\V{E}}{}}
         {\pair{\DSigma{\V{X}}{\DUnit}}{\V{D}}{}}{}
}\]
%
We simply add a constructor, $\SYMBDVar$, making its arguments
$\DSigma{\V{X}}{\DUnit}$---just an element of $\V{X}$.
$\V{E}$ and $\V{D}$ stay put, leaving the other constructors
unchanged. Unfolding the interpretation of this definition, we
find an extended sum, corresponding to the \(\V{X}\D{+}\) in
$\FMFreeMonadD$. Taking the fixpoint ties the knot and we have our
data.

\begin{wstructure}
<- A generic program: monadic substitution [equation]
    <- subst : \forall T X Y. mu ([T]* X) -> (X -> mu ([T]* Y)) -> mu ([T]* Y)
        -> Using Fold
\end{wstructure}

\newcommand{\subst}{\F{subst}}
\newcommand{\apply}{\F{apply}}

Now we need the operations. As expected, \(\LAM{\x}\DVar{\x}\)
plays the r\^ole of \return, making variables terms. Meanwhile,
\bind is indeed \emph{substitution}, which we now
implement generically, making use of $\cata$. Let us write the type,
and start filling in the blanks:
%
\[\stk{
\begin{array}{@{}ll}
\subst : & \PITEL{\V{D}}{\TagDesc}
           \PI{\V{X}, \V{Y}}{\Set}
           (\V{X} \To \mude{(\FreeMonad{\V{D}}{\V{Y}})}) \To \\
         & \mude{(\FreeMonad{\V{D}}{\V{X}})} \To
           \mude{(\FreeMonad{\V{D}}{\V{Y}})}
\end{array} \\
\subst\: \V{D}\: \V{X}\: \V{Y}\: \V{\sigma} \mapsto
  \cata\: (\toDesc{(\FreeMonad{\V{D}}{\V{X}})})\:
          (\mude{(\FreeMonad{\V{D}}{\V{Y}})})\:
          \SHED
}\]
%
We are left with implementing the algebra of the catamorphism. Its
role is to catch appearances of $\DVar{\V{x}}$ and replace them by
$\V{\sigma}\: \V{x}$. This corresponds to the following definition:
%
\[\stk{
\begin{array}{@{}ll}
\apply : & \PITEL{\V{D}}{\TagDesc}
           \PI{\V{X}, \V{Y}}{\Set}
           (\V{X} \To \mude{(\FreeMonad{\V{D}}{\V{Y}})}) \To \\
         & \descop{\toDesc{(\FreeMonad{\V{D}}{\V{X}})}}{(\mude{(\FreeMonad{\V{D}}{\V{Y}})})}
           \To  \mude{(\FreeMonad{\V{D}}{\V{Y}})}
\\
\end{array} \\
\begin{array}{@{}l@{\:\mapsto\:\:}l}
\apply\: \V{D}\: \V{X}\: \V{Y}\: \V{\sigma}\: \sqr{\DVar{\V{x}}}   & \V{\sigma}\: \V{x}                   \\
\apply\: \V{D}\: \V{X}\: \V{Y}\: \V{\sigma}\: \pair{\V{c}}{\V{xs}}{} & \Con{\pair{\V{c}}{\V{xs}}{}}
\end{array}
}\]

\begin{wstructure}
    -> Consequences
        <- We have free monad datatype
            <- Term + variables
        <- We have monad operations
            <- Return / var
            <- Substitution / bind
\end{wstructure}

We complete the hole with $\apply\: \V{D}\: \V{X}\: \V{Y}\:
\V{\sigma}$. Every tagged
description can be seen as a signature of operations: we can uniformly
add a notion of variable, building a new type from an old one, then
providing the substitution structure.

\begin{wstructure}
<- Deriving new data-structure and functions on them
    <- Computing the Free Monad of a datatype
        <- Derive new data-structure from previous one
            <- It is just code
        /> New data-structure comes with some equipment
    <- Computing new functions on computed datatypes
        <- If data comes with structure, we ought to be able to capture it
            <- Induction on Desc
            -> Ability to compute over data
\end{wstructure}

%% Candidate for removal:
%% With the free monad construction, we have seen two kinds of generic
%% operations. Firstly, we have derived a new data-structure from another
%% one: we make the free monad from its underlying functor. To do so, we
%% crucially rely on the fact that datatypes are nothing but codes. We
%% are therefore entitled to modify this code and, in this case, extend
%% it. Extending a datatype might give rise to a more structured object,
%% as was the case here.  So, secondly, we have equipped this new
%% datatype with its inherent structure: the \bind\ and
%% \return\ operations. We have been able to build them as generic
%% functions.

\subsection{Skyhooks all the way up?}

%% * Desc in Desc
%% ** /> Blur the lines between implementation and reflection
%% ** /> Abusing the paradoxical nature of Set : Set

In this section, we have seen how to \emph{levitate}
descriptions. Although our theory, as presented here, takes
$\Bhab{\Set}{\Set}$, our annotations indicate how a stratified theory
could code each level from above.  We do not rely on the paradoxical
nature of $\Bhab{\Set}{\Set}$ to flatten the hierarchy of descriptions
and fit large inside small.  We shall now be more precise about what
we have done.

%% * Summary
%% ** <- What is the equipment for making data-types
%% ** <- What is reflected, what is implemented
%% ** <- (table 1)

Let us first clarify the status of the implementation. The kit for
making datatypes is presented in Table~\ref{tab:sumup-operators}. For
each operation, we describe its role and its status, making clear
which components are self-described and which ones are actually
implemented.

%% * Paradox
%% ** <- How do we bottom out in a stratified setting?
%% ** <- Spiral
%% ***    <- Encoding of DescD_n : Desc_{n+1}
%% ***    <- Desc_n = Mu DescD_n : Set_{n+1}
%% ** <- Agda model: Desc_42
%% ***    <- No dependent pattern-matching
%% ***    <- No IR
%% ***    <- Normal universes
%% ***    -> Straight Agda (UTT)
%% ** <- ``Self-encoding'' only in a level polymorphic sense
%% ***    -> Agda model: set poly

In a stratified system, the `self-encoded' nature of $\Desc$ appears
only in a set polymorphic sense: the principal type of the encoded
description generalises to the type of $\Desc$ itself. We encode this
much in our set polymorphic model in Agda and in our Coq model,
crucially relying on typical ambiguity~\cite{harper:implicit-universe}.
We step outside current technology only to replace the declared $\Desc$
with its encoding.

Even this last step, we can approximate within a standard predicative
hierarchy. Fix a top level, perhaps \(42\). We may start by declaring
$\Desc^{42}:\Set^{43}$. We can then construct $\DescD^{41}:\Desc^{42}$
and thus acquire an encoded $\Desc^{41}$. Although $\Desc^{41}$ is
encoded, not declared, it includes the relevant descriptions,
including $\DescD^{40}$. We can thus build the tower of descriptions
down to $\Desc^0$, encoding every level below the top.  Description of
descriptions forms a `spiral', rather than a circle. We have modelled
this process exactly in Agda, without any appeal to dependent
pattern matching, induction-recursion, or set polymorphism. All it
takes to build such a sawn-off model of encodings is inductive
definition and a cumulative predicative hierarchy of set levels.

\begin{table}

{
\begin{center}
\begin{tabular}{|c|c|c|c|}
\hline
Object                & Role                        & Status \\
\hline
\hline
$\EnumU$              & Build finite sets           & Levitated \\
\hline
$\Desc$               & Describe pattern functors   & Levitated \\
\hline
$\SYMBdescop{\_}$     & Interpret descriptions      & Hardwired \\
\hline
$\SYMBMu$, $\SYMBCon$ & Define, inhabit fixpoints   & Hardwired \\
\hline
$\SYMBind$, $\SYMBAll$, $\SYMBall$
                      & Induction principle         & Hardwired \\
\hline
\end{tabular}
\end{center}
}

\caption{Summary of constructions on Descriptions}
\label{tab:sumup-operators}

\end{table}


\section{Introduction}
\label{sec:Introduction}

\newcommand{\firstUseGoal}[1]
  %% {\emph{#1}}
  %% {\textbf{#1}}
  {\textbf{\emph{#1}}}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% \rkc{Part 1.1: Motivation and SAL/CAL/FPF. Editing TBD.}

Conventionally, the design of data structures and algorithms is chiefly focused on performance, which is of significantly diminished concern in proof assistants.
%
As such, proof assistants often use entirely different data structures than those of conventional settings, with a focus on simplicity or useful proof-theoretic properties.

Consider dictionaries, which---among a broad range of purposes---are often used for implementing type contexts and evaluation environments.
%
The most basic representation for a dictionary, an \emph{\sal}, comprises a list of key-value pairs~\citep[Lists]{Pierce:SF1}.
%
Association lists are simple to create, manipulate, and reason about.
%
But because they allow duplicate bindings for the same key and they are sensitive to the order of insertions, many distinct \sal{}s represent the same semantic mapping.
%
For example, first two lines of \autoref{fig:intro-example}
%
%% ---and illustrated below in \autoref{fig:uneq}---
%
show two distinct association lists (among others) to represent a dictionary with three particular bindings.
%
This lack of \firstUseGoal{\Extensional} can make proofs more difficult~\cite[Maps]{Pierce:SF1} or impossible, especially when it comes to proving \emph{contraction} and \emph{exchange}~\citep{StructProp}
%
as discussed further in \autoref{sec:CaseStudy} and \autoref{sec:Discussion:Generality}.

%% \input{fig-unequal}

\input{fig-summary} %% here to force layout on p.2

To establish a one-to-one correspondence between association lists and semantic mappings, one solution~\citep{FMapList} is to maintain a \emph{canonical}
%
form that is semantically valid and unique, namely, a list in which there are no duplicate keys and, furthermore, where keys are in sorted order.%
%
\footnote{\hspace{0.01in}%
%
The Coq standard library~\citep{FMapInterface}, describes unordered but deduplicated \sal{}s as ``weak,'' implying that \cal{}s are ``strong.''
%
}
%
The second association list described in \autoref{fig:intro-example} is one such example.
%
This approach is \extensional{}, that is, it permits using built-in equality for semantic equivalence.
%
However, this approach is not \firstUseGoal{\semanticallyTotal}; the underlying list type allows arbitrary, possibly invalid lists, so a proof of validity is required to refine the coarser type.
%
%% However, it requires extending the underlying list type---which allows arbitrary, possibly invalid lists---with a proof of validity that refines the coarser type.
%
The downsides of having to use validity proofs are discussed further in \autoref{sec:CaseStudy} and \autoref{sec:Discussion:Generality}.

A third conventional solution is to represent dictionaries as finite partial functions~\cite[Maps]{Pierce:SF1}.
%
The third line of \autoref{fig:intro-example} depicts a nested $\lambda$-expression that serves as the ``lookup table'' (we omit the ``Some'' constructors for space).
%
Assuming \emph{functional extensionality}~\mbox{\cite[Logic]{Pierce:SF1}}---which can be postulated in Coq and Agda without introducing a contradiction, and is a theorem in some newer proof assistants built on a cubical logical framework---this representation is also \extensional{}.
%
However, a plain function type does not rule out terms that are \emph{non-}finite maps, possibly requiring additional proofs of validity as with canonical association lists.\footnote{\hspace{0.01in}%
%
This is often not a problem in practice:
%
if not iterated or destructed, an infinite dictionary will work just as well,
%
so a finiteness proof is unnecessary.
%
%Additionally, traversing a finite program text will create only a finite number of mappings.
}
%
Worse are the facts that, unlike either association list representation, partial functions lack \firstUseGoal{\DecidableEq} and cannot be \firstUseGoal{\destructed}.
%
The function type could be refined with its domain---\ie{}~a canonical list of keys---but would then suffer the same drawbacks of validity proofs \`{a} la canonical association lists.

In some cases, the aforementioned drawbacks are minor or can be worked around.
%
But developing large proofs is challenging, so any stumbling block can cause exorbitant increases in verbosity, time, effort, and accidental complexity.
%
Furthermore, as shown in \autoref{sec:CaseStudy}, there are cases where these drawbacks make a mechanization task not merely difficult, but outright impossible.

%% Because the representations of semantically equivalent dictionaries are not necessarily equal, proofs involving association lists may not use a built-in equality to distinguish them.
%%
%% - are typically implemented in one of the following manners: (TODO source for each)

%% The \sal~ (\SAL) solution is perhaps the most common, being trivially simple. Furthermore:
%% %\begin{itemize}
%%  (1) any \SAL~ that type-checks represents a valid mapping,
%%        so there's no need to refine the dict with a proof term that asserts its validity,
%%  (2) it's trivial to destruct and iterate,
%%  (3) checking semantic equivalence of two dicts, while non-trivial, is decidable and not too difficult.
%% %\end{itemize}

%% Since performance is often not a concern,
%% we have an opportunity to reassess the core criteria by which data structures are judged. Chiefly, these
%% new criteria will focus on alleviating or eliminating difficulties that crop up in the development of
%% proofs. In the \nameref{sec:Problem} section, we discuss several of these properties and argue for their
%% conceptual and practical importance.

%% The \cal~ (\CAL) and \fpf~ (\FPF) solutions (as well as ours) are set-like - i.e. they are insensitive
%% to duplication and ordering of insertions. As such, semantically equivalent dicts are also equal according
%% to the built-in equality primitive\footnote{Finite partial functions require the additional postulate of
%% \emph{functional extensionality}, which can be postulated
%% at essentially no cost given that it's been proven consistent with the constructive calculi underlying most
%% popular proof assistants (TODO SOURCE)}. However, \CAL~ and \FPF~ have other drawbacks.
%% If used as data, a \CAL~ must be refined with a proof of validity to ensure that it is ordered and deduped,
%% while equality is not decidable for {\FPF}s. In the \nameref{sec:Problem} section we discuss the difficulties
%% that arise from these drawbacks.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% \rkc{Part 1.2: Summary of Design Goals. Editing TBD.}

Ideally, when working in a proof assistant, an implementation of a data structure---dictionaries in particular for this paper---would satisfy the following properties:

\newcommand{\designGoal}[1]
  {\textbf{\emph{#1:}}}

\begin{enumerate}

\item
%
\designGoal{\SemTot}
%
Every term in the representation type is semantically valid, \ie{}~the mapping from terms to their semantic meanings is total.

\item
%
\designGoal{\SemInj}
%
Built-in equality corresponds to semantic equivalence, \ie{} two unequal terms have different semantic meanings.

\item
%
\designGoal{\EqDec}
%
Built-in equality is decidable for the representation type.

\end{enumerate}

Furthermore, in addition to properties about the external interface of the data structure, it is often useful to retain the ability to inspect, iterate, and manipulate sub-dictionaries.
%
%% Thus, our final design goal:

\begin{enumerate}

\item[4.]
%
\designGoal{\EzDstr}
%
The ability to decompose a data structure into atomic subparts in a convenient manner.

%% to facilitate

%% sufficiently

\end{enumerate}

\autoref{fig:prop-summary} summarizes the preceding discussion along these dimensions; note that the association list representations can be easily destructed, but destruction is not possible for partial functions.
%
None of the conventional representations satisfies both \SemTot{} and \SemInj{}, nor more than three of the four properties.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% \rkc{Part 1.3: Delta Dicts. Editing TBD.}

\parahead{A New Representation}
%
We offer a solution, dubbed \emph{\dds{}}, which achieves the first three of the desired properties, and partially achieves the fourth.
%
%% A \dd{} is similar to a canonical association list, but instead of storing each literal key value, it stores the \emph{difference} from the previous key, minus 1.
%
A \dd{} can be described as a ``canonical-by-construction'' association list: instead of storing each literal key value, it stores the \emph{difference} from the previous key, minus 1 (details in \autoref{sec:DD}).
%
For example, compare the canonical association list and \dd{} for the \autoref{fig:intro-example} example:

\vsepRule

%% keep this in sync with Figure 1
\begin{tabular}{ l l }
 \Cal{} & [(1, \str{a}), (3, \str{b}), (6, \str{c})] \\
 \Dd{}  & [(1, \str{a}), (1, \str{b}), (2, \str{c})]
\end{tabular}

\vsepRule

Every well-typed list-of-pairs is a valid \dd{} (\SemTot), thus no proof term is needed to establish validity.
%
Every unique \dd{} represents a unique semantic mapping, thus built-in equality may be used for semantic equivalence (\SemInj).
%
%% there is a bijection between \dd{} terms and finite maps,
%
Furthermore, we define a function which determines whether or not two \dds{} are equal (\EqDec), and a \texttt{destruct} function, which permits destruction albeit in a more awkward manner than standard pattern matching.

As summarized in \autoref{fig:prop-summary}, delta dictionaries strike a new balance in this design space.
%
Compared to \cal{}s, \dds{} enjoy \SemTot{}---the lone property among those we identify which the \cal{} does not.
%
However, destruction and iteration for \dds{} is substantially more difficult.
%
Furthermore, unlike all of the conventional representations, \dds{} require a bijection to the naturals, not merely decidable equality or ordering, for their key types.
%
%% Our definition and implementation uses natural numbers for keys, and we illustrate how to use bijections to the naturals as a way of supporting strings, integers, or other key types that can be bijected to the naturals without great difficulty.
%
Lastly, though not a concern from a client's perspective, the implementation of delta dictionaries is considerably more involved than the conventional approaches.

%% than it is with the {\SAL}s or {\CAL}s, for which these operations are trivial ({\FPF}s cannot be properly destructed at all).

%% Naturally, \dds~ have some drawbacks as well.

%% Although most types that are suitable for use as keys in the first place can be bijected to the
%% naturals, for some types defining this bijection may be too awkward or cumbersome, in which case \dds~ may
%% be a poor choice.

%% As with the drawbacks of the other solutions, this one is
%% discussed further in the \nameref{sec:Problem} section.

\parahead{Outline}
%
%% Next, we describe the core operations for delta dictionaries in \autoref{sec:DD} and the relevant metatheory in \autoref{sec:DD:props}.
%
Next, we describe the core operations for delta dictionaries and the relevant metatheory in \autoref{sec:DD}.
%
In \autoref{sec:CaseStudy}, we describe a small case study in proof development that demonstrates the necessity of delta dictionaries.
%
Finally, we conclude in \autoref{sec:Discussion} with a discussion. %% of related work.
%
%% Our implementation and proofs are formalized in Agda and available in the anonymous supplementary materials.

%% {\color{gray}
%%
%% \subsection{\rkc{Grab Bag of Text}}
%%
%% %% \section{Problem}
%% %% \subsection{Problem}
%% %% \label{sec:Problem}
%%
%% \rkc{1}
%%
%% Data structures can possess or lack a wide range of properties that make them easier or harder to work with
%% when proving metatheory. Naturally, when possible, data structures should be chosen that posess those
%% properties which make the task at hand easier. Achieving this requires identifying the valuable properties,
%% what it is that makes them valuable, and finding or inventing data structures which possess those properties.
%% We hold that this is a broad topic of
%% research, which we illustrate in the particular context of choosing an implementation for a dictionary. In
%% this context, we identify, according to both conceptual and practical considerations, four important and
%% discriminating properties that a dictionary implementation may or may not possess:
%% TODO there's a good chance some of these properties have been named somewhere else. We should make sure we
%% don't try and coin terms for anything that's already been defined somewhere.
%%
%% % TODO we may decide that the conceptual and practical benefits of decidable equality are so obvious or
%% % self-evident that we don't need any further discussion of that property
%%
%% %% \subsection{Conceptual Importance}
%% %% \label{sec:Problem:concept}
%%
%% \rkc{2}
%%
%% In \autoref{sec:Problem:pract}, we illustrate practical problems and benefits under the particular context
%% of choosing an implementation for dictionaries. But first, it's worth exploring conceptual aspects that
%% apply to the general case. These conceptual aspects capture the spirit of practical considerations and
%% motivate the search for solutions that are not merely serviceable but elegant, moral, and insightful.
%%
%% \subsubsection{\SemTot}
%% \label{sec:Problem:concept:SemTot}
%% TODO something something "making impossible states impossible", i think allusion to the right parts out of that
%% video will suffice to make the point here
%%
%% \subsubsection{\SemInj}
%% \label{sec:Problem:concept:SemInj}
%% TODO something something it yields arbitrariness, which is not always wrong but which is unaesthetic and, when
%% avoidable, undesirable. It also allows for a semantic meaning to be represented by a concrete thing that is
%% needlessly verbose and complicated.
%%
%% \subsubsection{\EqDec}
%% \label{sec:Problem:concept:EqDec}
%% The languages of proof assistants generally pride themselves on being total and constructive, on emphasizing
%% the knowable and demonstrable over the mysterious and hypothetical.
%%
%% \subsubsection{\EzDstr}
%% \label{sec:Problem:concept:EzDstr}
%% TODO We need to clearly explain what we're talking about here, and clarify stuff like "facilitate inspection"
%% and "unrestricted non-additive manipulation". Aside from these clarifications, we won't say much else - in
%% this case the conceptual value largely boils down to the practical value.
%%
%% %Finally, because they guarantee the \emph{structural properties} \emph{contraction} and
%% %\emph{exchange}, \dds~ are inherently inappropriate for substructural logics/judgments that reject one or
%% %both of those properties. In these cases, the naive solution's sensitivity to duplication and/or ordering is
%% %a feature, not a bug, and that solution becomes not only the natural solution but the morally correct one.
%% %Future work could explore the possibility of data structures that uphold one of these properties but not the
%% %other.
%%
%% }


\documentclass[11pt,a4paper]{report}
\usepackage{amsmath,amsfonts,amssymb,amsthm,epsfig,epstopdf,titling,url,array}
\usepackage{enumitem}
\usepackage{changepage}
\usepackage{graphicx}
\usepackage{caption}
\usepackage{listings}
\usepackage{color}
\theoremstyle{plain}
\newtheorem{thm}{Theorem}[section]
\newtheorem{lem}[thm]{Lemma}
\newtheorem{prop}[thm]{Proposition}
\newtheorem*{cor}{Corollary}
\theoremstyle{definition}
\newtheorem{defn}{Definition}[section]
\newtheorem{conj}{Conjecture}[section]
\newtheorem{exmp}{Example}[section]
\newtheorem{exercise}{Exercise}[section]
\theoremstyle{remark}
\newtheorem*{rem}{Remark}
\newtheorem*{note}{Note}
\def\changemargin#1#2{\list{}{\rightmargin#2\leftmargin#1}\item[]}
\let\endchangemargin=\endlist

\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}

\lstdefinestyle{mystyle}{
	backgroundcolor=\color{backcolour},
	commentstyle=\color{codegreen},
	keywordstyle=\color{magenta},
	numberstyle=\tiny\color{codegray},
	stringstyle=\color{codepurple},
	basicstyle=\footnotesize,
	breakatwhitespace=false,
	breaklines=true,
	captionpos=b,
	keepspaces=true,
	numbers=left,
	numbersep=5pt,
	showspaces=false,
	showstringspaces=false,
	showtabs=false,
	tabsize=2
}

\lstset{style=mystyle}
\begin{document}

\section*{Problem}
Flush with his success in the hospitality business, Moe decides to follow Bezos and Musk into more celestial pursuits.  But instead of something easy like rocket science, he tries his hand at pure mathematics.  According to theorem stated by Pierre de Fermat in the 17th century but not proven until the end of the 20th century, the following equation has no solutions in the integers for $n>2$:
$$a^n + b^n = c^n$$
So for example, the theorem means that there  are no three integers $a$, $b$ and $c$ such that $a^3 + b^3 = c^3.$
\\\\
Moe thinks the 20th century proof is ``fake math" and he claims to have found a counter-example.  He says that with $a = 91$, $b=56$ and $c=121$ there is an $n$ such that the equation holds.  Joe asks what $n$ is and Moe says he did it in his head and forgot.  Joe thinks a little and says that there can't be any such $n$ for those three numbers.  Explain why Joe is right.
\\\\
This problem was adapted from a problem from Car Talk.

\newpage
\section*{Solution}
The key here is to look at the prime factorization of $a$, $b$ and $c$.
\\
$a = 7 \times 13$
\\
$b = 7 \times 2^3$
\\
$c = 11^2$
\\
Now suppose that for some $n$, $a^n + b^n = c^n$.  That means  \\ $(7  \times 13)^n + (7 \times2^3)^n = (11^2)^n$.  Now you can factor out the $7$ from the terms on the left and distribute the exponents, giving $7^n(13^n + 2^{3n}) = 11^{2n}$.  But that is not possible because that would imply that $7$ is a factor of $11^{2n}$ which is impossible by uniqueness of prime factorization.

\end{document}


\documentclass{article}

\usepackage{amsmath}
\usepackage{graphicx}
\usepackage{subcaption}

\title{Cardano's Formula for Cubic Equations}
\author{Ananmeti Zion Umoh}
\date{November 18, 2021}

\begin{document}
	\pagenumbering{gobble}
	\maketitle
	\pagenumbering{arabic}
	\begin{center}
		\textbf{Abstract}
	\end{center}
\begin{center}

	Gerolamo Cardano was born in Pavia in 1501 as the illegitimate\\
	child of a jurist. He attended the University of Padua and became\\
	a physician in the town of Sacro, after being rejected by his home
	town of Milan. He became one of the most famous doctors in all of\\
	Europe , having treated the Pope. he was also an astrolger and an\\
	avid gambler, to which he wrote the Book on Games of Chnace, which was th first serious treatise on the mathematics of probabiltiy [1].\\
\end{center}
\section{\textbf{Introduction to Cardano's Formula}}

Cardano's formula for solution of cubic equations for an equation like;
\begin{align*}
	& x^2 + a_1x^2 + a_2x+a^3 = 0\\
\end{align*}
the parameter Q, R, S and T can be computed thus,
\begin{align*}
	&Q=\frac{3a_2-a^2}{a}
	&R=\frac{9a_1a_2-27a^3-2a^3}{54}
\end{align*}
\begin{align*}
	&S=3\sqrt{R+\sqrt{-Q^3+R^2}}
	&T=\sqrt{R-\sqrt{-Q^3+R^2}}
\end{align*}
to give the roots;
\begin{align*}
	&x_1=S+T-\frac{1}{3}a_1\\
	&x_2=\frac{-(S+T)}{2}-\frac{a_1}{3}+i\frac{\sqrt{3}(s-T)}{2}-\frac{a_!}{3}+i\frac{\sqrt{3}(s-T)}{2}\\	&x_2=\frac{-(S+T)}{2}-\frac{a_1}{3}+i\frac{\sqrt{3}(s-T)}{2}-\frac{a_!}{3}+i\frac{\sqrt{3}(s-T)}{2}
\end{align*}
\begin{align*}
	Note: &x_3 must not have a co-efficient.
\end{align*}
\subsection{Some Examples}
\begin{enumerate}
			\item &x^3-3x^2+4=0
			\item &2x^3+6x^2+1=0
\end{enumerate}

\paragraph{\textbf{References}}
|1| P. Scarani, "Out of control: vita di gerolamo cardano (1501-1576),"
Pathologica, vol. 93, pp. 565-574, 2001.

\end{document}


\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{color}
\usepackage{amsmath}
\usepackage{algorithm}
\usepackage{algorithmic}
\usepackage{pgfplots}
\usetikzlibrary{patterns}

\makeatletter
\newcommand\resetstackedplots{
\makeatletter
\pgfplots@stacked@isfirstplottrue
\makeatother
\addplot [forget plot,draw=none] coordinates{(10K,0) (100K,0) (1000K,0)};
}
\makeatother

\makeatletter
\newcommand\resetstackedplotsOne{
\makeatletter
\pgfplots@stacked@isfirstplottrue
\makeatother
\addplot [forget plot,draw=none] coordinates{(1,0) (2,0) (4,0) (8,0) (16,0)};
}
\makeatother

\newtheorem{theorem}{Theorem}[section]
\newtheorem{definition}{Definition}[section]
\newcommand{\R}{\mathbb{R}}

\title{Parla Performance Study}
%\author{Milinda Fernando and Geroge Biros}

\begin{document}
\maketitle

\section{Benchmark}
We use the tall skinny QR (TSQR) decomposition algorithm proposed in \cite{benson2013direct,demmel2008communication}. For a given matrix $A\in \R^{m\times n}$ we assume $m>>n$. The parallel TSQR algorithm can be summarized as follows.

\begin{algorithm}
    \caption{TSQR decomposition}
    \begin{algorithmic}
    \REQUIRE $A \in \R^{m\times n}, m>>n$, $p$ parallel tasks
    \ENSURE $A=QR$ for $Q\in \R^{m\times n }$, $Q^TQ=I$ and $R$ is upper triangular.
    \STATE $Ar \leftarrow Partition(A,p)$
    \STATE $Q1r,R1r \leftarrow QR(Ar)$
    \STATE $R1 \leftarrow Gather(R1r,p)$
    \STATE $Q2,R \leftarrow Qr(R1)$
    \STATE $Q2r \leftarrow Partition(Q2r)$ \COMMENT{Same parition bounds as $A$}
    \STATE $Q\leftarrow Q1r \times Q2r$
    \RETURN $Q,R$
    \end{algorithmic}
\end{algorithm}

\section{Implementation}
The following 3 implementations are considered in this performance evaluation.
\begin{itemize}
    \item Parla QR + Cupy
    \item Python thread pool execution + Cupy
    \item Python MPI + Cupy
\end{itemize}

\section{Experimental setup}
\begin{itemize}
    \item The experiments are conducted in Frontera, GPU cluster, where a single node consists of Two Intel Xeon E5-2620 v4 (“Broadwell”) with four NVIDIA Quadro RTX5000 GPUs.
\end{itemize}

\section{Results}
% \subsection{Performance analysis}
% \begin{itemize}
%     \item In Parla, for a given problem size, overall performance is dependent on the number of task created. I think the optimal performance depends on the balance between the task creation overhead and ability to overlap execution in tasks (i.e., overlap between data movement and computations)
%     \item Parla runtime grows with the number of tasks in task space for smaller problem sizes. The above is expected, since the overhead of the task creation and scheduling increases with the number of tasks.
% \end{itemize}

\subsection{Parla, shared memory, and distributed memory comparison}
This  section presents the key result, which compares the Para shared memory multi-GPU implementation with hand-coded, python shared memory multi-GPU and python distributed memory TSQR implementations. Figure \ref{fig:parla_overall} presents the overall TSQR runtime while the cost breakdown for the each case presented in figure \ref{fig:parla_breakdown}. We keep the number of columns in the matrix fixed at 100 columns for all the Parla comparisons. For the Parla comparisons, the QR decomposition is done as follows.
\begin{itemize}
    \item Partitioned block qr : performed in GPU $[Q_p,R_p]=qr(A_p)$, only $R_p$ factor is return to the host.
    \item Gather $R_p$ to $R$, and QR on cpu, $[Q2,R]=qr(R)$
    \item Scatter $Q2$ to $Q2_p$, matrix matrix multiplication on GPU.
\end{itemize}

The performance difference between the 3 approaches can be summarized as follows.
\begin{itemize}
    \item Why matmult task has different timings?
    \begin{itemize}
        \item The final $Q$ matrix stays distributed for the MPI implementation, i.e.,  hence not concatenated for the final $Q$ matrix.
        \item For Parla, the final Q global matrix is assembled, where the result of the matmult is directly written to the task portion of the global memory.
        \item In the thread-based parallelism case, the global Q matrix is assembled, but unlike the Parla, it is not directly written to the $Q$ global memory, hence has the cost of an additional data copy cost.
    \end{itemize}
\end{itemize}

\begin{figure}[H]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
                ybar,
                symbolic x coords={1K,10K,100K,1000K},
                xtick=data,
                width=12cm,
                height=4.5cm,
                grid=major,
                xlabel={number of rows $\rightarrow$}, ylabel={runtime (s) $\rightarrow$},
                %legend pos= north west,
                legend columns=3,
                legend style={at={(0.5,-0.35)},anchor=north},
                bar width=0.25cm,
            ]
            \addplot[bar shift =-0.3cm,fill=red!20] table[x={Rows},y={total_max}]{dat/frontera_mpi_gpu_v3.dat};
            \addplot[bar shift = 0.0cm,fill=blue!20] table[x={Rows},y={total}]{dat/frontera_sm_gpu.dat};
            \addplot[bar shift = 0.3cm,fill=orange!20] table[x={Rows},y={total}]{dat/frontera_parla_gpu_t4.dat};
            %\addplot[bar shift=0.2cm,fill=red!30] table[x={Rows},y={total}]{dat/frontera_parla_gpu_t8.dat};
            %\addplot[bar shift=0.4cm,fill=yellow!30] table[x={Rows},y={total}]{dat/frontera_parla_gpu_t16.dat};
            \legend{MPI+Cupy, Threads+Cupy, Parla+Cupy}
        \end{axis}
    \end{tikzpicture}
    \caption{Single node performance for TSQR algorithm implemented using Cupy where the parallelism was achieved by Python MPI, Python threading and Parla framework in Frontera GPU cluster. Each approach decompose the problem to 4 partitioned tasks, where the created tasks are assigned to its own GPU. For the above study, matrix column size was fixed at 500 columns.\label{fig:parla_overall}}
\end{figure}
\begin{figure}[H]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
                ybar stacked,
                symbolic x coords={10K,100K,1000K},
                xtick=data,
                width=12cm,
                height=4.5cm,
                grid=major,
                xlabel={number of rows $\rightarrow$}, ylabel={runtime (s) $\rightarrow$},
                %legend pos= north west,
                legend columns=4,
                legend style={at={(0.5,-0.35)},anchor=north},
                bar width=0.25cm,
            ]
            \addplot[bar shift=-0.3cm,fill=red!30] table[x={Rows},y={qr1_max}]{dat/frontera_mpi_gpu_v3.dat};
            \addplot[bar shift=-0.3cm,fill=blue!30] table[x={Rows},y={qr2_max}]      {dat/frontera_mpi_gpu_v3.dat};
            \addplot[bar shift=-0.3cm,fill=orange!30] table[x={Rows},y={mm_max}]    {dat/frontera_mpi_gpu_v3.dat};
            \addplot[bar shift=-0.3cm,fill=green!20] table[x={Rows},y={mpi_comm_max}]{dat/frontera_mpi_gpu_v3.dat};
            \addplot[bar shift=-0.3cm,fill=black!20] table[x={Rows},y={overhead}]    {dat/frontera_mpi_gpu_v3.dat};
            \resetstackedplots
            \addplot[postaction={pattern=north east lines},bar shift=-0.0cm,fill=red!30] table[x={Rows},y={qr1}]       {dat/frontera_sm_gpu.dat};
            \addplot[postaction={pattern=north east lines},bar shift=-0.0cm,fill=blue!30] table[x={Rows},y={qr2}]      {dat/frontera_sm_gpu.dat};
            \addplot[postaction={pattern=north east lines},bar shift=-0.0cm,fill=orange!30] table[x={Rows},y={mm}]     {dat/frontera_sm_gpu.dat};
            \addplot[postaction={pattern=north east lines},bar shift=-0.0cm,fill=black!20] table[x={Rows},y={overhead}]{dat/frontera_sm_gpu.dat};
            \resetstackedplots
            \addplot[postaction={pattern=dots},bar shift=0.3cm,fill=red!30] table[x={Rows},y={qr1}]       {dat/frontera_parla_gpu_t4.dat};
            \addplot[postaction={pattern=dots},bar shift=0.3cm,fill=blue!30] table[x={Rows},y={qr2}]      {dat/frontera_parla_gpu_t4.dat};
            \addplot[postaction={pattern=dots},bar shift=0.3cm,fill=orange!30] table[x={Rows},y={mm}]     {dat/frontera_parla_gpu_t4.dat};
            \addplot[postaction={pattern=dots},bar shift=0.3cm,fill=black!20] table[x={Rows},y={overhead}]{dat/frontera_parla_gpu_t4.dat};
        \legend{qr1(MPI), qr2(MPI),mm(MPI),MPI\_comm,other(MPI),qr1(Threads),qr2(Threads), mm \& Q (Threads),other(threads),qr1(Parla),qr2(Parla), mm \& Q(Parla), other(Parla)}
        \end{axis}
    \end{tikzpicture}
    % \caption{Single node performance for TSQR algorithm implemented using Cupy where the parallelism achieved by, Python MPI, Python threading and Parla framework in Frontera GPU cluster. For this experiment matrix column size was fixed at 100 columns.}
    \caption{Overall cost breakdown for MPI + Cupy, Threads + Cupy and Parla using 4 blocks on 4 GPUs. \texttt{qr1} denotes the first QR decomposition on the GPU, \texttt{q2} denotes the second QR decomposition on the CPU, and \texttt{mm + Q} denotes the GPU matrix-matrix multiplication and the final $Q$ matrix computation. The timing difference for \texttt{mm \& Q}, mainly because, for MPI Q matrix stays distributed hence not gathered, for Parla and thread-based cases, the final $Q$ is assembled, but thread-based case uses one additional data copy in comparison to Parla.
    \label{fig:parla_breakdown} }
\end{figure}

\subsection{Multi-node TSQR performance comparison}
This section presents, the weak and strong scaling results for distributed memory TSQR factorization. In the following experiments, the distributed memory parallelism is handled by MPI, and the shared memory based multi-GPU parallelism is achieved through parla. In MPI+Parla hybrid parallelism, MPI processes is launched for each node, with four threads per process. In pure MPI + Cupy based distributed parallelism case, four MPI tasks are launched for each node.

\textbf{MPI gather operation and the second QR factorization}: In TSQR, the $R1r$ factors need to be gathered from the processors to perform the second QR factorization. The above operation has the complexity of $\mathbb{O}(pn^2\log(p))$. The input matrix size for the second QR factorization is $pn\times n$. Therefore the gather and the second QR factorization grows with $p$ for fixed $n$.

\textbf{Scaling plots}:
For the scaling plots (see Figures \ref{fig:parla_ws_n100}, \ref{fig:parla_ws_n1000}, \ref{fig:parla_ss_n100}, and \ref{fig:parla_ss_n1000}) for the ease of comparison overall cost breakdown follows the Parla tasks.

\textbf{Results discussion}:

\begin{itemize}
    \item \textbf{MPI +Parla} : For each node 1 MPI task is launched with \texttt{OMP\_NUM\_THREADS}=16, and each process will create 4 tasks, 1 for each GPU on the node.
    \item \textbf{MPI +Cupy} : For each node 4 MPI tasks are launched with \texttt{OMP\_NUM\_THREADS}=4, and each process will utilize its corresponding GPU assigned.
\end{itemize}

\begin{itemize}
    \item Why qr2 has bad scaling ?
    \begin{itemize}
        \item The input matrix size for the qr2 is $pn\times n$, where $p$ is the number of MPI tasks, and $n$ is the number of the columns. MPI + Parla has a smaller qr2 decomposition with more OMP threads, while in MPI + Parla case will have a bigger qr2 factorization with a smaller number of OMP threads. The above is the main reason for bad strong scalability for n=1000, MPI + Cupy case.
    \end{itemize}
    \item \texttt{MM \& Q} computation
    \begin{itemize}
        \item As mentioned in MPI, there is no global Q assembly between distributed memory processes. Therefore, for MPI + Cupy case it is only the matrix-matrix multiplication while in MPI + Parla case, the global $Q$ matrix is not assembled but the shared memory $Q$ matrix is concatenated with a copy operation.
    \end{itemize}
    \item Why \texttt{qr1} is expensive in MPI + Parla case ?
    \begin{itemize}
        \item Distributed memory matrix partitioning or matrix creation is not included in the overall time.
        \item For MPI + Cupy case, once the execution begins in the local partition there is no additional partitioning step involved.
        \item For MPI + Parala case, once the execution begins there is an additional shared memory block wise matrix partitioning cost associated with the qr1 task.
    \end{itemize}
\end{itemize}

For the scaling plots the maximum time across all the MPI processors is used.

\begin{itemize}
    \item Figure \ref{fig:parla_ws_n100} : Weak scaling results keeping number of columns fixed at 100.
    \item Figure \ref{fig:parla_ws_n1000} : Weak scaling results keeping number of columns fixed at 1000.
    \item Figure \ref{fig:parla_ss_n100} : Strong scaling results for fixed matrix size of $1.6M\times 100$.
    \item Figure \ref{fig:parla_ss_n1000} : Strong scaling results for fixed matrix size of $1.6M\times 1000$.
\end{itemize}

\begin{figure}[H]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
                ybar stacked,
                symbolic x coords={1,2,4,8,16},
                xtick=data,
                width=12cm,
                height=4.5cm,
                grid=major,
                xlabel={number of nodes $\rightarrow$}, ylabel={runtime (s) $\rightarrow$},
                %legend pos= north west,
                legend columns=3,
                legend style={at={(0.5,-0.35)},anchor=north},
                bar width=0.3cm,
            ]
            \addplot[bar shift=0.0cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c100_ws_gz_100K.dat};
            \addplot[bar shift=0.0cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c100_ws_gz_100K.dat};
            \addplot[bar shift=0.0cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c100_ws_gz_100K.dat};
            \addplot[bar shift=0.0cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c100_ws_gz_100K.dat};
            \addplot[bar shift=0.0cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c100_ws_gz_100K.dat};

            \resetstackedplotsOne
            \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_mpi_c100_ws_gz_100K.dat};
            \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_mpi_c100_ws_gz_100K.dat};
            \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_mpi_c100_ws_gz_100K.dat};
            \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_mpi_c100_ws_gz_100K.dat};
            \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_mpi_c100_ws_gz_100K.dat};

            \resetstackedplotsOne
            \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c100_ws_100K_16.dat};
            \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c100_ws_100K_16.dat};
            \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c100_ws_100K_16.dat};
            \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c100_ws_100K_16.dat};
            \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c100_ws_100K_16.dat};

            \legend{qr1(MPI+Cupy), qr2(MPI+Cupy), MM(MPI+Cupy), MPI\_Comm(MPI+Cupy), other (MPI+Cupy), qr1(MPI+Parla), qr2(MPI+Parla), MM(MPI+Parla), MPI\_Comm (MPI+Parla), other(MPI+Parla), qr1(MPI+Parla 16t), qr2(MPI+Parla 16t), MM(MPI+Parla 16t), MPI\_Comm (MPI+Parla 16t), other(MPI+Parla 16t),}
        \end{axis}
    \end{tikzpicture}
    \caption{The figure presents the weak scalability results for 100K rows per node and the number of columns fixed at 100 for MPI + Parla based hybrid parallelism and MPI + Cupy based implementations across 16 nodes (64 GPUs) in the Frontera GPU cluster. Note that for Parla+Cupy case, tasks are dynamically created and scheduled at the runtime, where in MPI+Cupy tasks creation and execution statically coded in the implementation.  \label{fig:parla_ws_n100}  }
\end{figure}
\begin{figure}[H]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
            ybar stacked,
            symbolic x coords={1,2,4,8,16},
            xtick=data,
            width=12cm,
            height=4.5cm,
            grid=major,
            xlabel={number of nodes $\rightarrow$}, ylabel={runtime (s) $\rightarrow$},
            %legend pos= north west,
            legend columns=3,
            legend style={at={(0.5,-0.35)},anchor=north},
            bar width=0.3cm,
        ]
        \addplot[bar shift=0.0cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c1000_ws_gz_100K.dat};
        \addplot[bar shift=0.0cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c1000_ws_gz_100K.dat};
        \addplot[bar shift=0.0cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c1000_ws_gz_100K.dat};
        \addplot[bar shift=0.0cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c1000_ws_gz_100K.dat};
        \addplot[bar shift=0.0cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c1000_ws_gz_100K.dat};

        \resetstackedplotsOne
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_mpi_c1000_ws_gz_100K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_mpi_c1000_ws_gz_100K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_mpi_c1000_ws_gz_100K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_mpi_c1000_ws_gz_100K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_mpi_c1000_ws_gz_100K.dat};

        \resetstackedplotsOne
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c1000_ws_100K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c1000_ws_100K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c1000_ws_100K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c1000_ws_100K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c1000_ws_100K_16.dat};

        \legend{qr1(MPI+Cupy), qr2(MPI+Cupy), MM(MPI+Cupy), MPI\_Comm(MPI+Cupy), other (MPI+Cupy), qr1(MPI+Parla), qr2(MPI+Parla), MM(MPI+Parla), MPI\_Comm (MPI+Parla), other(MPI+Parla), qr1(MPI+Parla 16t), qr2(MPI+Parla 16t), MM(MPI+Parla 16t), MPI\_Comm (MPI+Parla 16t), other(MPI+Parla 16t),}
    \end{axis}
    \end{tikzpicture}
    \caption{The figure presents the weak scalability results for 100K rows per node and the number of columns fixed at 1000 for MPI + Parla based hybrid parallelism and MPI + Cupy based implementations across 16 nodes (64 GPUs) in the Frontera GPU cluster. Note that for Parla+Cupy case, tasks are dynamically created and scheduled at the runtime, where in MPI+Cupy tasks creation and execution statically coded in the implementation.\label{fig:parla_ws_n1000} }
\end{figure}
\begin{figure}[H]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
            ybar stacked,
            symbolic x coords={1,2,4,8,16},
            xtick=data,
            width=12cm,
            height=4.5cm,
            grid=major,
            xlabel={number of nodes $\rightarrow$}, ylabel={runtime (s) $\rightarrow$},
            %legend pos= north west,
            legend columns=3,
            legend style={at={(0.5,-0.35)},anchor=north},
            bar width=0.3cm,
        ]
        \addplot[bar shift=0.0cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c100_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c100_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c100_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c100_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c100_ss_1600K_4.dat};

        \resetstackedplotsOne
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_mpi_c100_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_mpi_c100_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_mpi_c100_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_mpi_c100_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_mpi_c100_ss_1600K.dat};

        \resetstackedplotsOne
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c100_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c100_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c100_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c100_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c100_ss_1600K_16.dat};

        \legend{qr1(MPI+Cupy), qr2(MPI+Cupy), MM(MPI+Cupy), MPI\_Comm(MPI+Cupy), other (MPI+Cupy), qr1(MPI+Parla), qr2(MPI+Parla), MM(MPI+Parla), MPI\_Comm (MPI+Parla), other(MPI+Parla), qr1(MPI+Parla 16t), qr2(MPI+Parla 16t), MM(MPI+Parla 16t), MPI\_Comm (MPI+Parla 16t), other(MPI+Parla 16t),}
    \end{axis}
    \end{tikzpicture}
    \caption{The figure presents the strong scalability results for a fixed matrix size of $1.6M \times 100$ for MPI + Parla based hybrid parallelism and MPI + Cupy based implementations across 16 nodes (64 GPUs) in the Frontera GPU cluster. Note that for Parla+Cupy case, tasks are dynamically created and scheduled at the runtime, where in MPI+Cupy tasks creation and execution statically coded in the implementation. \label{fig:parla_ss_n100}
    }
\end{figure}
\begin{figure}[H]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
            ybar stacked,
            symbolic x coords={1,2,4,8,16},
            xtick=data,
            width=12cm,
            height=4.5cm,
            grid=major,
            xlabel={number of nodes $\rightarrow$}, ylabel={runtime (s) $\rightarrow$},
            %legend pos= north west,
            legend columns=3,
            legend style={at={(0.5,-0.35)},anchor=north},
            bar width=0.3cm,
        ]
        \addplot[bar shift=0.0cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c1000_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c1000_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c1000_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c1000_ss_1600K_4.dat};
        \addplot[bar shift=0.0cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c1000_ss_1600K_4.dat};

        \resetstackedplotsOne
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_mpi_c1000_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_mpi_c1000_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_mpi_c1000_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_mpi_c1000_ss_1600K.dat};
        \addplot[postaction={pattern=north east lines}, bar shift=-0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_mpi_c1000_ss_1600K.dat};

        \resetstackedplotsOne
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=red!20]    table[x={nodes},y={qr1_max}]        {dat/frontera_parla_c1000_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=yellow!20] table[x={nodes},y={qr2_max}]        {dat/frontera_parla_c1000_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=cyan!20]   table[x={nodes},y={mm_max}]         {dat/frontera_parla_c1000_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=green!20]  table[x={nodes},y={mpi_comm_max}]   {dat/frontera_parla_c1000_ss_1600K_16.dat};
        \addplot[postaction={pattern=dots}, bar shift=0.4cm,fill=black!20]  table[x={nodes},y={overhead}]       {dat/frontera_parla_c1000_ss_1600K_16.dat};

        \legend{qr1(MPI+Cupy), qr2(MPI+Cupy), MM(MPI+Cupy), MPI\_Comm(MPI+Cupy), other (MPI+Cupy), qr1(MPI+Parla), qr2(MPI+Parla), MM(MPI+Parla), MPI\_Comm (MPI+Parla), other(MPI+Parla), qr1(MPI+Parla 16t), qr2(MPI+Parla 16t), MM(MPI+Parla 16t), MPI\_Comm (MPI+Parla 16t), other(MPI+Parla 16t),}
    \end{axis}
    \end{tikzpicture}
    \caption{The figure presents the strong scalability results for a fixed matrix size of $1.6M \times 1000$ for MPI + Parla based hybrid parallelism and MPI + Cupy based implementations across 16 nodes (64 GPUs) in the Frontera GPU cluster.Note that for Parla+Cupy case, tasks are dynamically created and scheduled at the runtime, where in MPI+Cupy tasks creation and execution statically coded in the implementation. \label{fig:parla_ss_n1000}
    }
\end{figure}

\subsection{Single GPU performance analysis}
This section presents a single GPU performance analysis to perform the QR decomposition of varying matrix sizes.
\begin{figure}[!tbhp]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
                symbolic x coords={1K,10K,100K,1000K},
                xtick=data,
                width=12cm,
                height=5cm,
                grid=major,
                xlabel={number of rows $\rightarrow$}, ylabel={GB/s $\rightarrow$ },
                %legend pos=outer north east,legend columns=1,
                legend columns=3,
                legend style={at={(0.5,-0.3)},anchor=north},
            ]
            \addplot[mark=*,red] table[x={NROWS},y={H2D_BW(GB/sec)}]{dat/rtx5000_qr_n10.dat};
            \addplot[mark=square,blue] table[x={NROWS},y={H2D_BW(GB/sec)}]{dat/rtx5000_qr_n100.dat};
            \addplot[mark=triangle,green] table[x={NROWS},y={H2D_BW(GB/sec)}]{dat/rtx5000_qr_n1000.dat};
            \addplot[mark=*,red,dashed] table[x={NROWS},y={D2H_BW(GB/sec)}]{dat/rtx5000_qr_n10.dat};
            \addplot[mark=square,blue,dashed] table[x={NROWS},y={D2H_BW(GB/sec)}]{dat/rtx5000_qr_n100.dat};
            \addplot[mark=triangle,green,dashed] table[x={NROWS},y={D2H_BW(GB/sec)}]{dat/rtx5000_qr_n1000.dat};

            \legend{H2D (n=10),H2D (n=100), H2D (n=1000),D2H (n=10),D2H (n=100), D2H (n=1000)}
        \end{axis}
    \end{tikzpicture}
    \caption{Empirical data transfer bandwidth for cupy transfer of $2d$ numpy arrays. For a matrix $A$ with $m$ rows and $n$ columns, host to device (H2D) transfer has the complexity of $\mathbb{O}(mn)$. The device to host (D2H) transfers copy two cupy arrays (computed $Q,R$ factors) to numpy arrays with the complexity of $\mathbb{O}(mn + n^2)$.}
\end{figure}
\begin{figure}[!bhp]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
                symbolic x coords={1K,10K,100K,1000K},
                xtick=data,
                width=12cm,
                height=5cm,
                grid=major,
                xlabel={number of rows $\rightarrow$ }, ylabel={GFlops/s $\rightarrow$},
                %legend pos=outer north east,legend columns=1,
                legend columns=3,
                legend style={at={(0.5,-0.3)},anchor=north},
            ]
            \addplot[mark=*,red] table[x={NROWS},y={QR_FLOPS(GFlops/sec)}]{dat/rtx5000_qr_n10.dat};
            \addplot[mark=square,blue] table[x={NROWS},y={QR_FLOPS(GFlops/sec)}]{dat/rtx5000_qr_n100.dat};
            \addplot[mark=triangle,green] table[x={NROWS},y={QR_FLOPS(GFlops/sec)}]{dat/rtx5000_qr_n1000.dat};
            \legend{n=10,n=100,n=1000}
        \end{axis}
    \end{tikzpicture}
    \caption{GFlops/s attained by \texttt{cupy.linalg.qr} where GFlops/s is computed based on $\mathbb{O}(2mn^2 - \frac{2}{3}n^3)$ floating point operations for different matrix sizes, on a single RTX5000 GPU node, where $n$ denotes the number of columns.}
\end{figure}
\begin{figure}[!tbhp]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
                symbolic x coords={1K,10K,100K,1000K},
                xtick=data,
                width=12cm,
                height=6cm,
                grid=major,
                xlabel={number of rows $\rightarrow$ }, ylabel={GFlops/s $\rightarrow$},
                %legend pos=outer north east,legend columns=1,
                legend columns=3,
                legend style={at={(0.5,-0.25)},anchor=north},
            ]
            \addplot[mark=*,red] table[x={NROWS},y={QR_FLOPS(GFlops/sec)}]{dat/frontera_rtx_cpu_qr_n10.dat};
            \addplot[mark=square,blue] table[x={NROWS},y={QR_FLOPS(GFlops/sec)}]{dat/frontera_rtx_cpu_qr_n100.dat};
            \addplot[mark=triangle,green] table[x={NROWS},y={QR_FLOPS(GFlops/sec)}]{dat/frontera_rtx_cpu_qr_n1000.dat};
            \legend{n=10,n=100,n=1000}
        \end{axis}
    \end{tikzpicture}
    \caption{GFlops/s attained by \texttt{numpy.linalg.qr} where GFlops/s is computed based on $\mathbb{O}(2mn^2 - \frac{2}{3}n^3)$ floating point operations for different matrix sizes, on frontera RTX partition CPU node, where $n$ denotes the number of columns.}
\end{figure}
\begin{figure}[!bhp]
    \centering
    \begin{tikzpicture}
        \begin{axis}[
                symbolic x coords={1K,10K,100K,1000K},
                xtick=data,
                width=12cm,
                height=6cm,
                grid=major,
                xlabel={number of rows $\rightarrow$ }, ylabel={GFlops/s $\rightarrow$},
                %legend pos=outer north east,legend columns=1,
                legend columns=3,
                legend style={at={(0.5,-0.25)},anchor=north},
            ]
            \addplot[mark=*,red] table[x={NROWS},y={MM_FLOPS(GFlops/sec)}]{dat/rtx5000_mm_n10.dat};
            \addplot[mark=square,blue] table[x={NROWS},y={MM_FLOPS(GFlops/sec)}]{dat/rtx5000_mm_n100.dat};
            \addplot[mark=triangle,green] table[x={NROWS},y={MM_FLOPS(GFlops/sec)}]{dat/rtx5000_mm_n1000.dat};
            \legend{n=10,n=100,n=1000}
        \end{axis}
    \end{tikzpicture}
    \caption{GFlops/s  attained by \texttt{cupy.matmult} where  GFlops/s is computed based on $\mathbb{O}(mn(2n-1))$ floating point operations for different matrix sizes, on a single RTX5000 GPU node, where $n$ denotes the number of columns.}
\end{figure}

\subsection{Performance model for shared memory multi-gpu TSQR}
Let $B_{H2D}$, $B_{D2H}$ be the bandwidth for the host to device and device to host data transfers, respectively. Let $QR_{gpu}(m,n)$ and $QR_{cpu}(m,n)$ be the flops/s for the cupy QR and numpy QR decomposition for a given number of rows $m$ and columns $n$. Let $MM_{gpu}(m,n)$ be the flops/s for GPU execution of general matrix-matrix multiplication. Let p be the number of threads (1 per GPU) in the shared memory parallel multi-GPU execution. Then the overall time can be approximated by,
\begin{align}
    T(m,n,p) &= T(\frac{m}{p}, n)_{qr1,GPU}  + T(np,n)_{qr2,CPU} + T(\frac{m}{p},m)_{mm,GPU} \label{eq:qr_sm}
\end{align} where we can write,
\begin{equation}
    T(\frac{m}{p}, n)_{qr1,GPU} = \frac{mn}{pB_{H2D}} + \frac{\frac{2mn^2}{p} -\frac{2}{3}n^3}{QR_{gpu}} + \frac{n^2}{B_{D2H}} \label{eq:qr1_sm}
\end{equation}
\begin{equation}
    T(np,n)_{qr2,CPU} = \frac{2pn^3 -\frac{2}{3}n^3}{QR_{cpu}} \label{eq:qr2_sm}
\end{equation}
\begin{equation}
    T(\frac{m}{p},m)_{mm,GPU} = \frac{n^2}{B_{H2D}} + \frac{\frac{mn}{p}(2n-1)}{MM_{gpu}} + \frac{mn}{pH_{D2H}} \label{eq:mm_sm}
\end{equation}

Tables \ref{tb:qr1} and \ref{tb:mm} presents the predicted time for partition QR and matrix-matrix multiplication respectively. For partitioned QR (see Table \ref{tb:qr1}), the difference between presented and reported runtime is almost constant because collecting of $R_p$ matrix from mapped execution is fixed because the size of $R_p$ is independent of the number of rows in the input matrix.

In contrast with partitioned QR, the final step of the TSQR algorithm is performing a matrix-matrix multiplication to get the final $Q$ matrix. The collection overhead of final $Q_p=Q1_pQ2_p$ matrix from mapped thread pool execution grows with the number of rows in the input matrix, since $Q_p$ is $\frac{m}{p} \times n$ matrix.

\begin{table}[!tbhp]
    \begin{center}
        \resizebox{\textwidth}{!}{
        \begin{tabular}{ |c|c|c|c|c|c|c|c|c| }
         \hline
         Rows & Cols & H2D(GB/s) &  D2H(GB/s)  &  QR(Flops/s) &  t1 (s) (runtime) & t2(s) (predicted) & t1-t2 & (t1-t2)/t1\\
         \hline
            1000	& 100 & 2.13 & 	1.12  &	2.35	& 0.014914022	& 0.001948	& 0.012966022 & 0.869384662 \\
            10000	& 100 & 4.77 &  3.95  &	12.23	& 0.017084167	& 0.00443	& 0.012654167 & 0.740695579 \\
            100000	& 100 & 4.71 &  6.08  &	22.21	& 0.045916252	& 0.026449	& 0.019467252 & 0.423973017 \\
            1000000	& 100 & 4.57 &  2.86  &	34.87	& 0.212147208	& 0.184173	& 0.027974208 & 0.131862249 \\
         \hline
        \end{tabular}}
    \end{center}
    \caption{Table presents the predicted QR1 runtime (see (\ref{eq:qr1_sm})) based on the empirically compute bandwidths and flops/s for matrix rows of $\frac{m}{p} \times n$, where $p$ denotes the number of GPUs used for the actual computation. The difference between the predicted and the reported runtime is almost a constant.\label{tb:qr1}}
\end{table}

\begin{table}[!tbhp]
    \begin{center}
        \resizebox{\textwidth}{!}{
        \begin{tabular}{ |c|c|c|c|c|c|c|c|c| }
         \hline
         Rows & Cols & H2D(GB/s) &  D2H(GB/s)  &  MM(Flops/s) &  t1 (s) (runtime) & t2(s) (predicted) & t1-t2 & (t1-t2)/t1\\
         \hline
            1000	& 100 & 2.29	& 2.40 & 	28.38   &	0.00404992	& 0.000185	& 0.00386492  & 0.954320085\\
            10000	& 100 & 4.85	& 4.97 & 	160.31  &	0.005135891	& 0.000358	& 0.004777891 & 0.930294471\\
            100000	& 100 & 4.98	& 6.66 &    209.65  &	0.011140349	& 0.002726	& 0.008414349 & 0.755303893\\
            1000000	& 100 & 4.21	& 2.91 & 	225.91  &	0.119347156	& 0.030051	& 0.089296156 & 0.748205144\\
         \hline
        \end{tabular}}
    \end{center}
    \caption{Table presents the predicted MM runtime (see (\ref{eq:mm_sm})) based on the empirically compute bandwidths and flops/s for matrix rows of $\frac{m}{p} \times n$, where $p$ denotes the number of GPUs used for the actual computation. The difference between the predicted and the reported runtime grows with $\frac{m}{p}$. \label{tb:mm}}
\end{table}

\bibliographystyle{plain}
\bibliography{parla_qr}

\end{document}


\documentclass[aspectratio=169, draft]{beamer}
\usepackage{mathtools}
\usetheme{Berlin}

\def\S{\textbf{S}}
\def\K{\textbf{K}}
\def\I{\textbf{I}}
\def\Y{\textbf{Y}}

\newcommand{\DefineTerm}[2]{#1\Coloneqq&\ #2\\}
\newcommand{\Case}[1]{\lvert\ &\ #1\\}

\title{Compiling to the Categorical Abstract Machine}
\author{Nathan Ringo}

\begin{document}

\frame{\titlepage}

\section{Combinators}

\begin{frame}
	\frametitle{Combinators -- What?}
	\begin{columns}[T]
		\column{0.45\textwidth}
		\begin{itemize}
			\item<1-> Unnamed Representation
			\item<2-> Y
			\begin{itemize}
				\item<3-> SKI
				\item<6-> Turing Complete!
			\end{itemize}
		\end{itemize}

		\column{0.55\textwidth}
		\begin{description}
			\item<2->{$\Y x \rightarrow x (\Y x)$}
			\item<3->{$\S xyz \rightarrow xz\left(yz\right)$}
			\item<4->{$\K xy \rightarrow x$}
			\item<5->{$\I x \rightarrow x$}
			\item<6->{$\S\I\I x \rightarrow \I x\left(\I x\right)\rightarrow xx$}
		\end{description}
	\end{columns}
\end{frame}

\begin{frame}
	\frametitle{Combinators -- Why?}
	\begin{columns}[T]
		\column{0.45\textwidth}
		\begin{itemize}
			\item<1-> Simple Semantics
			\item<2-> Simple Machine Code
			\item<2-> Peephole Optimization
		\end{itemize}

		\column{0.55\textwidth}
		\only<2->{\includegraphics[width=2.5in]{slides/peephole-paper.png}}
		\note{1989 paper}
	\end{columns}
\end{frame}

\section{The CAM}

\begin{frame}
	\frametitle{The CAM}
	\begin{itemize}[<+->]
		\item Compiling functional languages
		\item First-class environments as reversed LL
		\item De Bruijn indices
	\end{itemize}
\end{frame}

\begin{frame}
	\frametitle{De Bruijn Indices}
	\begin{columns}[T]
		\column{0.4\textwidth}
		\textbf{Lambda Calculus}
		\begin{align*}
			\DefineTerm{e}{x}
			\Case{\lambda x.\,e}
			\Case{e_1 e_2}
		\end{align*}
		\vspace{-0.5cm}
		\only<3->{\textbf{With De Bruijn Indices}
		\begin{align*}
			\DefineTerm{e}{n}
			\Case{\lambda e}
			\Case{e_1 e_2}
		\end{align*}}

		\column{0.7\textwidth}
		\begin{description}
			\vspace{0.25cm}
			\item<2->{$\left(\lambda x.\,x\right)\left(\lambda x.\,x\right)$}
			\item<2->{$\lambda x.\,\lambda y.\,x$}
			\item<2->{$\lambda f.\,\left(\lambda x.\,f\left(xx\right)\right)\left(\lambda x.\,f\left(xx\right)\right)$}
			\vspace{1.75cm}
			\item<4->{$\left(\lambda 0\right)\left(\lambda 0\right)$}
			\item<4->{$\lambda\lambda 1$}
			\item<4->{$\lambda\left(\lambda 1\left(00\right)\right)\left(\lambda 1\left(00\right)\right)$}
		\end{description}
	\end{columns}
\end{frame}

\begin{frame}
	\frametitle{The CAM}
	\begin{itemize}
		\item Compiling functional languages
		\item First-class environments as reversed LL
		\item De Bruijn indices
		\item ``Static'' and ``Dynamic'' Combinators
	\end{itemize}
\end{frame}

\begin{frame}
	\frametitle{Combinators}
	\begin{columns}[T]
		\column{0.24\textwidth}
		``Dynamic''
		\begin{itemize}
			\item<1-> Application
			\item<2-> $\left(x\texttt{,}y\right)$
		\end{itemize}
		\only<3->{``Static''}
		\begin{itemize}
			\item<4-> \texttt{Fst}
			\item<4-> \texttt{Snd}
			\item<5-> $\left<x\texttt{,}y\right>$
			\item<6-> $x \circ y$
			\item<7-> \texttt{\$}, $\Lambda\left(x\right)$
			\item<8-> $\texttt{'}x$
		\end{itemize}

		\column{0.41\textwidth}
		\vspace{2.1cm}
		\begin{itemize}
			\item<4-> $\texttt{Fst}\left(x\texttt{,}y\right)\rightarrow x$
			\item<4-> $\texttt{Snd}\left(x\texttt{,}y\right)\rightarrow y$
			\item<5-> $\left<x\texttt{,}y\right>z\rightarrow\left(xz\texttt{,}yz\right)$
			\item<6-> $\left(x \circ y\right)z\rightarrow x\left(yz\right)$
			\item<7-> $\texttt{\$}\left(\Lambda\left(x\right)y\texttt{,}z\right)\rightarrow x\left(y\texttt{,}z\right)$
			\item<8-> $\texttt{'}xy = x$
		\end{itemize}
	\end{columns}
\end{frame}

\begin{frame}
	\frametitle{Compilation Rules}
	\begin{itemize}[<+->]
		\item $\texttt{compile (}e_1\ e_2\texttt{) = }\texttt{\$}\circ\left<\texttt{compile }e_1, \texttt{compile }e_2\right>$
		\item $\texttt{compile (}\lambda e\texttt{) = }\Lambda\left(\texttt{compile }e\right)$
		\item $\texttt{compile (\$0) = Snd}$
		\item $\texttt{compile (\$(S\,}n\texttt{)) = compile (\$\,}n\texttt{)}\circ\texttt{Fst}$
		\item $\texttt{compile (}c\texttt{) = '}c$
	\end{itemize}
	\only<6->{Apply the program to any constant to run it}
\end{frame}

\section{Examples}

\def\App{\mathtt{\$}}
\newcommand\Apply[2]{\left(#1\right)\left(#2\right)}
\newcommand\Compose[2]{#1\circ#2}
\newcommand\DPair[2]{\left(#1, #2\right)}
\def\Fst{\mathtt{Fst}}
\newcommand\Lam[1]{\Lambda\left(#1\right)}
\newcommand\Pair[2]{\left<#1, #2\right>}
\newcommand\Quote[1]{\texttt{'#1}}
\def\Snd{\mathtt{Snd}}

\begin{frame}
	\frametitle{Compilation Example of $\mathtt{\left(\lambda x:int.\ \lambda y:int.\ x\right)\ 0\ 1}$}
	\begin{itemize}[<+->]
		\item Source: $\mathtt{\left(\lambda x:int.\ \lambda y:int.\ x\right)\ 0\ 1}$
		\item De Bruijn: $\mathtt{\left(\lambda\lambda\$1\right)\ 0\ 1}$
		\item CAM: $\Compose{\App}{\Pair{\Compose{\App}{\Pair{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{0}}}}{\Quote{1}}}$
	\end{itemize}
\end{frame}

\begin{frame}
	\frametitle{Runtime Example of $\Compose{\App}{\Pair{\Compose{\App}{\Pair{ \Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{0}}}}{\Quote{1}}}$}
	\begin{itemize}[<+->]
		\item $\Apply{\Compose{\App}{\Pair{\Compose{\App}{\Pair{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{0}}}}{\Quote{1}}}}{\Quote{e}}$
		\item $\Apply{\App}{\Apply{\Pair{\Compose{\App}{\Pair{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{0}}}}{\Quote{1}}}{\Quote{e}}}$
		\item $\Apply{\App}{\DPair{
			\Apply{\Compose{\App}{\Pair{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{0}}}}{\Quote{e}}
		}{\Apply{\Quote{1}}{\Quote{e}}}}$
		\item $\Apply{\App}{\DPair{
			\Apply{\Compose{\App}{\Pair{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{0}}}}{\Quote{e}}
		}{1}}$
		\item $\Apply{\App}{\DPair{
			\Apply{\App}{\Apply{
				\Pair{\Lam{\Lam{\Compose{\Snd}{\Fst}}}
			}{
				\Quote{0}
			}}{\Quote{e}}}
		}{1}}$
		\item $\Apply{\App}{\DPair{
			\Apply{\App}{\DPair{
				\Apply{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{e}}
			}{
				\Apply{\Quote{0}}{\Quote{e}}
			}}
		}{1}}$
		\item $\Apply{\App}{\DPair{
			\Apply{\App}{\DPair{
				\Apply{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{e}}
			}{0}}
		}{1}}$
	\end{itemize}
\end{frame}

\begin{frame}
	\frametitle{Runtime Example of $\Compose{\App}{\Pair{\Compose{\App}{\Pair{ \Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{0}}}}{\Quote{1}}}$ (Pt. 2)}
	\begin{itemize}[<+->]
		\item $\Apply{\App}{\DPair{
			\Apply{\App}{\DPair{
				\Apply{\Lam{\Lam{\Compose{\Snd}{\Fst}}}}{\Quote{e}}
			}{0}}
		}{1}}$
		\item $\Apply{\App}{\DPair{
			\Apply{
				\Lam{\Compose{\Snd}{\Fst}}
			}{
				\DPair{\Quote{e}}{0}
			}
		}{1}}$
		\item $\Apply{\Compose{\Snd}{\Fst}}{\DPair{
			\DPair{\Quote{e}}{0}
		}{
			1
		}}$
		\item $\Apply{\Snd}{\Apply{\Fst}{\DPair{
			\DPair{\Quote{e}}{0}
		}{
			1
		}}}$
		\item $\Apply{\Snd}{\DPair{\Quote{e}}{0}}$
		\item $0$
	\end{itemize}
\end{frame}

\end{document}


\documentclass[a4paper]{article}

\def\npart{II}
\def\ntitle{Integrable Systems}
\def\nlecturer{C.\ Warnick}

\def\nterm{Michaelmas}
\def\nyear{2017}

\input{header}

\begin{document}

\input{titlepage}

Consider
\[
  u_t + u_{xxx} =0
\]
Seek a solutioin of the form
\[
  u(x,t) = \exp(-i\omega t+ ikx)
\]
This solve the equation if
\[
  \omega = -k^3
\]
dispersion relation
so
\[
  u_k(x,t) = \exp(ik(k^t+x)
\]

Each \(u_k\) is a wave translating at speed \(k^2\) in negative \(x\)-direction. Since speed depends on frequency, we say the equation is \emph{dispersive}. We can consider a superposition of solutions
\[
  u(x,t) = \int_{\R}^{ } a(k)\exp(ik(k^2t+x) dk
\]
where \(a(k)\) is arbitrary and is related to \(\hat{u(\cdot,0)}\). Since different frequencies move at different speeds, solutions should spread out over time.

\subsection{Nonlinear Growth}

Consider
\[
  u_t - 6 uu_x =0,
\]
the \emph{Burgers equation}. It can be solved by the method of characteristics to give
\[
  u(x,t) = f(\xi), \zeta = x+6tf(\xi).
\]
and
\[
  f(\xi) = u(\xi,0).
\]

It can be shown that this solution becomes singular, in the sense that \(\sup_{x\in\R}|U_x(x,t)| \to \infty\) as \(t\to \infty\) where
\[
  T = \inf_{x\in\R} \frac{1}{6f'(x)}.
\]
Surprisingly, the dispersive and nonlinear growth effects balance for KdV. It was known for some time that KdV admits a soliton solution
\[
  u(x,t) = -2x^2 \sech^2 \chi (x-2x^4t-x_0)
\]
solves KdV for \(x,x_0\in \R\). It represents a wave moving to the right with speed \(4x^2\).

In the mid 60s, Kruskal and Zabosky simulated a solution that starts as two solitons, set up so that one overtakes the other. Curiously, these solitons passed through one another without interacting. If KdV were linear, this would be a surprise, but it is not.

\subsection{Sine-Gordon Equation}

The Sine-Gordon equation is the PDE
\[
  u_{tt} - u_{xx} + \delta uu = 0.
\]
This is the equation modelling an infinite series of pendula connected by springs.

Sine-Gordon also admits soliton solutions. The \(1\)-soliton solution is given by
\[
  u(x,t) = 4\tan^{-1} \exp \Big( \frac{x-vt}{\sqrt{1-v^2}} \Big)
\]
This is a ``kink'' solution. The wave translates uniformly with speed \(v\). As for KdV, soliton solutions pass through one another without interaction.

\subsection{Backlund Transformations}

Recall the Cauchy-Riemann equations
\begin{align*}
  u_x &= \sqrt y \\
  -u_y &= \sqrt x
\end{align*}
so that
\[
  u_{xx} + u_{yy} = 0.
\]

If we are given \(v\) a solution of Laplace, we can find \(u\) such that
\begin{align*}
  u_x &= \sqrt y \\
  u_y &= -\sqrt x
\end{align*}
Then \(u\) will automatically solve Laplace.

\begin{eg}
  Let \(v=xy\). Solve
  \begin{align*}
    u_x &= x \\
    u_y &= -y
  \end{align*}
This is an exmaple of a (auto) Backlund transformation, relating one solution to another by first order system.
\end{eg}

For a more interesting example, take Sine-Gordon in light cone coordinates
\[
  \frac{\p^2 u}{\p\xi \p\tau} = \sin u.
\]
Consider the equations
\begin{align*}
  \p_\xi(\phi_1-\phi_0) &= 2\varepsilon \sin \Big( \frac{\phi_1+\phi_0}{2} \Big) \\
  \p_\tau(\phi_1-\phi_0) &= \frac{2}{\varepsilon} \sin \Big( \frac{\phi_1-\phi_0}{2} \Big)
\end{align*}
Differentiating the first equation with repect to \(\tau\):
\begin{align*}
  \p_\psi\p_\xi(\phi_1-\phi_0) &= 2\varepsilon \p_\tau \frac{\phi_1+\phi_0}{2}\cos \Big( \frac{\phi_1+\phi_0}{2} \Big) \\
                               &= 2\sin\frac{\phi_1-\phi_0}{2} \cos\frac{\phi_1+\phi_0}{2} \\
                               &= \sin\phi_1 - \sin\phi_0
\end{align*}
so if \(\phi_0\) sovles Sine-Gordon then \(\phi_1\) must as well

\begin{ex}
  If \(\psi_0 = 0\), we recover the \(1\)-soliton solution.
\end{ex}
\end{document}