%------------------------------------------------------------------------------
\section{Process noise modelling}
\label{sec:noise}
%------------------------------------------------------------------------------

\subsection{Overview}

Describing the motion of a satellite through the integration of the equations of motion, considering all acting perturbations, will typically lead to
deviations with respect to the true trajectory. The first step is to account for the uncertainties in the initial state via the covariance matrix, which results from the orbit
determination process, essentially the errors in the observations of the satellite.

However, an additional source of error is often neglected: force model errors are introduced during the propagation and are manifold, including
\begin{itemize}
 \item unmodeled perturbations,
 \item uncertainties in the coefficients of the geopotential,
 \item geopotential series truncation,
 \item stochastic nature of solar and geomagnetic activity,
 \item uncertainty in the ballistic coefficient,
 \item simplifications in modeling the optical properties, etc.
\end{itemize}
Most of the above mentioned effects may be described by a stochastic process. For many systems, such a process is well approximated by Gaussian white noise and is referred to
as \gls{acr:snc}. A more sophisticated modeling, where process noise parameters are included in the state vector as solve-for parameters, is referred to as \gls{acr:dmc}.
Both methods are described in detail by \citet{tapley2004}.

However, as \cite{nazarenko2010} points out, analysis for the geopotential model shows that the resulting errors are apparently non-Gaussian, a property generally denoted as
\textit{colored noise}. The difference between colored and white noise results from the uncertainties in the coefficients of the geopotential as well as a truncation of the
infinite series of the geopotential (\eq{eq:geopotential}), introducing considerably time-correlated errors.

In the following, the model to consider colored noise due to the geopotential shall be described, based on the book of \cite{nazarenko2010}. Being only available in Russian
language, some more details of the model from that book are provided here for convenience. A similar approach is also provided by \cite{wright1981} or \cite{wright2008}, however,
the cross-correlation of the state vector and the process noise, as introduced in \sect{sec:propagation-covariance-theory}, are not taken into consideration for the
computations therein.

The main difference between modeling colored noise as opposed to white noise, is that the cross-correlation matrix between the state vector and the process noise is non-zero,
\begin{equation}
 \gls{sym:expVal}\left[\Delta \gls{sym:state}\left(\gls{sym:t}_0\right) \gls{sym:inputVec}\left(\gls{sym:t}\right)^T\right] \neq 0,
\end{equation}
so that, from \eq{eq:full-cov-prop}, besides the solution of the double integral for \gls{sym:Quu}, an additional integral for \gls{sym:Qxu} needs to be computed.

\subsection{Modelling the errors of the geopotential}

The errors in the geopotential model contribute to the expected value, or the second moment of process noise, as present in the integral formulations for \gls{sym:Quu} and
\gls{sym:Qxu}, which were defined in \eq{eq:full-cov-prop}. They are given here again, for convenience:
\begin{align}
  \gls{sym:Quu}\left(\gls{sym:t}\right) &= \int_{\gls{sym:t}_0}^{\gls{sym:t}} \int_{\gls{sym:t}_0}^{\gls{sym:t}} \gls{sym:set}\left(\gls{sym:t},\gls{sym:xi}\right) \cdot
\gls{sym:inputMat}\left(\gls{sym:xi}\right) \cdot
\gls{sym:expVal}\left[\gls{sym:inputVec}\left(\gls{sym:xi}\right)\cdot \gls{sym:inputVec}\left(\gls{sym:eta}\right)^T\right]
\gls{sym:inputMat}\left(\gls{sym:eta}\right)^T \cdot
\gls{sym:set}\left(\gls{sym:t},\gls{sym:eta}\right)^T d\gls{sym:xi} d\gls{sym:eta} \label{eq:quu}\\
\gls{sym:Qxu}\left(\gls{sym:t}\right) &=
\gls{sym:set}\left(\gls{sym:t},\gls{sym:t}_0\right)\cdot\int_{\gls{sym:t}_0}^{\gls{sym:t}}\gls{sym:expVal}\left[\Delta\gls{sym:state}\left(\gls{sym:t}_0\right)
\cdot\gls{sym:inputVec}\left(\gls{sym:eta}\right)^T\right]\cdot\gls{sym:inputMat}\left(\gls{sym:eta}\right)\cdot
\gls{sym:set}\left(\gls{sym:t},\gls{sym:eta}\right) d\gls{sym:eta}
\end{align}
The process noise function is given in an orbit-centered reference frame as a $3\times 1$ vector:
\begin{equation}
 \gls{sym:inputVec}\left(\gls{sym:t}\right) = \left(\Delta \gls{sym:specForce}_{\gls{idx:radial}}, \Delta \gls{sym:specForce}_{\gls{idx:along}}, \Delta
\gls{sym:specForce}_{\gls{idx:normal}}\right)^T, \label{eq:force-model-errors}
\end{equation}
here, $\Delta \gls{sym:specForce}_{\gls{idx:radial}}$ is the error in the force model in radial, $\Delta \gls{sym:specForce}_{\gls{idx:along}}$ in along-track and $\Delta
\gls{sym:specForce}_{\gls{idx:normal}}$ in orbit normal direction, respectively.

The following functions are introduced to simplify the formulas in the subsequent derivation:
\begin{align}
 \rhon{\gls{sym:geo_n}}\left(\gls{sym:r}\right)  &= \frac{\gls{sym:grav}}{\gls{sym:r}^2}\left(\frac{\re}{\gls{sym:r}}\right)^{\gls{sym:geo_n}}\\
 \fnk{\gls{sym:geo_n}}{\gls{sym:geo_m}} \left(\gls{sym:long}\right) &= \Delta\cnm{}{} \cos \left(\gls{sym:geo_m}\gls{sym:long}\right) + \Delta\snm{}{} \sin
\left(\gls{sym:geo_m}\gls{sym:long}\right) \\
 \gnk{\gls{sym:geo_n}}{\gls{sym:geo_m}} \left(\gls{sym:long}\right) &= \Delta\snm{}{} \cos \left(\gls{sym:geo_m}\gls{sym:long}\right) -
\Delta\cnm{}{} \sin \left(\gls{sym:geo_m}\gls{sym:long}\right),
\end{align}
where $\Delta\cnm{}{}$ and $\Delta\snm{}{}$ are desribing the model errors introduced through both, truncation (\textit{error of omission}), which means that the geopotential is
considered up to a maximum degree of $\gls{sym:geo_n}_{max}$, as well as uncertainties in the estimation of the coefficients of the harmonic series (\textit{error of commission}):
\begin{align}
 \Delta\cnm{}{},\Delta\snm{}{} = \begin{cases}
                                  \left(\cnm{}{}-\hat{\gls{sym:geo_C}}_{\gls{sym:geo_n}\gls{sym:geo_m}}\right),
\left(\snm{}{}-\hat{\gls{sym:geo_S}}_{\gls{sym:geo_n}\gls{sym:geo_m}}\right) & \mbox{if } \gls{sym:geo_n}\leq\gls{sym:geo_n}_{max} \\
                                  \cnm{}{}, \snm{}{} & \mbox{if } \gls{sym:geo_n}>\gls{sym:geo_n}_{max}
                                 \end{cases} \label{eq:deltaCS}
\end{align}
here, $\hat{\gls{sym:geo_C}}_{\gls{sym:geo_n}\gls{sym:geo_m}}$ and $\hat{\gls{sym:geo_S}}_{\gls{sym:geo_n}\gls{sym:geo_m}}$ are the nominal values, or best estimates, of the
harmonic coefficients. For the \acrshort{acr:eigen}-GL04C the mean value and the standard deviations of the coefficients are shown in \fig{fig:eigen-gl04c} as a
function of the geopotential degree \gls{sym:geo_n}.
\begin{figure}[h!]
  \centering
  \input{07-Tikz/eigen-gl04c_coefficients.tex}
  \caption{Mean value of the normalized spherical harmonic coefficients, as well as the normalized standard deviations as a function of the geopotential degree. Also
shown is
Kaula's approximation.\label{fig:eigen-gl04c}}
\end{figure}
The mean value for a certain degree \gls{sym:geo_n} is computed via a formulation given by \citet{kaula2000}:
\begin{equation}
 \gls{sym:mean}_{\gls{sym:geo_n}} = \sqrt{\frac{1}{2\gls{sym:geo_n}+1}\sum_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}} \left(\ncnm{}{}^2+\nsnm{}{}^2\right)}
\end{equation}
Kaula also provided an approximation to estimate the magnitude of the normalized coefficients, given the degree \gls{sym:geo_n}:
\begin{equation}
 \gls{sym:mean}_{\gls{sym:geo_n}} \approx \frac{\num{e-5}}{\gls{sym:geo_n}^2}. \label{eq:kaula}
\end{equation}
The standard deviation for the geopotential degree \gls{sym:geo_n} is computed according to \citet{nazarenko2010}:
\begin{equation}
 \gls{sym:stdev}_{\gls{sym:geo_n}}=\frac{1}{2\gls{sym:geo_n}+1}\sum_{\gls{sym:geo_m}+1}^{\gls{sym:geo_n}}\left(\gls{sym:stdev}\left(\ncnm{}{}\right)+
 \gls{sym:stdev}\left(\nsnm{}{}\right)\right).
\end{equation}

The force model errors, as required in \eq{eq:force-model-errors}, are obtained in two steps. The first step is to obtain the derivatives of the geopotential with respect to the
radius, the geocentric latitude and longitude, respectively (using only the perturbing part, see also \eq{eq:geopotential} and \eq{eq:deriv-geopotential}):
\begin{align}
 \Delta \gls{sym:specForce}_{\gls{idx:radial}} &=
       \Delta \left(\frac{\partial \gls{sym:potential}}{\partial \gls{sym:r}}\right) = -\sum\limits_{\gls{sym:geo_n}=2}^{\infty} \rhon{\gls{sym:geo_n}}\left(\gls{sym:r}\right)
\left(\gls{sym:geo_n}+1\right) \sum\limits_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}} \fnk{\gls{sym:geo_n}}{\gls{sym:geo_m}} \left(\gls{sym:long}\right) \legphi{}{} \label{eq:dfr}\\
 \Delta \gls{sym:specForce}_{\gls{idx:lat}}  &=
       \Delta \left(\frac{1}{\gls{sym:r}}\frac{\partial\gls{sym:potential}}{\partial\phigc}\right) = \sum\limits_{\gls{sym:geo_n}=2}^{\infty}
\rhon{\gls{sym:geo_n}}\left(\gls{sym:r}\right) \sum\limits_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}} \fnk{\gls{sym:geo_n}}{\gls{sym:geo_m}} \left(\gls{sym:long}\right)
\frac{\partial}{\partial \phigc} \legphi{}{} \label{eq:dfphi} \\
 \Delta \gls{sym:specForce}_{\gls{idx:lon}} &= \Delta \left(\frac{1}{\gls{sym:r}\cos\phigc}\frac{\partial\gls{sym:potential}}{\partial\gls{sym:long}}\right) =
    \sum\limits_{\gls{sym:geo_n}=2}^{\infty} \rhon{\gls{sym:geo_n}}\left(\gls{sym:r}\right) \sum\limits_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}}
      \frac{\gls{sym:geo_m}}{\cos\phigc}\gnk{\gls{sym:geo_n}}{\gls{sym:geo_m}} \left(\gls{sym:long}\right)\legphi{}{} \label{eq:dflam}
\end{align}
Assuming that the mean (expected value) of \eq{eq:dfr} through \eq{eq:dflam} is zero, the variance in radius, geocentric latitude and longitude direction, respectively, can be
obtained as:
\begin{align}
 \gls{sym:stdev}_{\gls{idx:radial}}^2 \left(\gls{sym:r},\phigc,\gls{sym:long}\right) &= \gls{sym:expVal}\left[\Delta \gls{sym:specForce}_{\gls{idx:radial}}^2\right]
       = \gls{sym:expVal}\left[\sum\limits_{j=2}^{\infty}\sum\limits_{\gls{sym:geo_n}=2}^{\infty} \rhon{j}\rhon{\gls{sym:geo_n}}
       \left(j+1\right)\left(\gls{sym:geo_n}+1\right) \sum\limits_{i=0}^{j}\sum\limits_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}}
\fnk{j}{i}\fnk{\gls{sym:geo_n}}{\gls{sym:geo_m}} \gls{sym:legendre}_{j,i}\gls{sym:legendre}_{\gls{sym:geo_n},\gls{sym:geo_m}}\right] \label{eq:sigmaRad}\\
 \gls{sym:stdev}_{\gls{idx:lat}}^2 \left(\gls{sym:r},\phigc,\gls{sym:long}\right) &= \gls{sym:expVal}\left[\Delta \gls{sym:specForce}_{\gls{idx:lat}}^2\right]
 = \gls{sym:expVal}\left[\sum\limits_{j=2}^{\infty}\sum\limits_{\gls{sym:geo_n}=2}^{\infty} \rhon{j}\rhon{\gls{sym:geo_n}}
       \sum\limits_{i=0}^{j}\sum\limits_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}}
\fnk{j}{i}\fnk{\gls{sym:geo_n}}{\gls{sym:geo_m}} \frac{\partial\gls{sym:legendre}_{j,i}}{\partial
\phigc}\frac{\partial\gls{sym:legendre}_{\gls{sym:geo_n},\gls{sym:geo_m}}}{\partial \phigc}\right]
       \\
 \gls{sym:stdev}_{\gls{idx:lon}}^2 \left(\gls{sym:r},\phigc,\gls{sym:long}\right) &= \gls{sym:expVal}\left[\Delta \gls{sym:specForce}_{\gls{idx:lon}}^2\right]
       = \gls{sym:expVal}\left[\sum\limits_{j=2}^{\infty}\sum\limits_{\gls{sym:geo_n}=2}^{\infty} \rhon{j}\rhon{\gls{sym:geo_n}}
       \sum\limits_{i=0}^{j}\sum\limits_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}} \frac{i\gls{sym:geo_m}}{\cos^2\phigc}\gnk{j}{i}\gnk{\gls{sym:geo_n}}{\gls{sym:geo_m}}
\gls{sym:legendre}_{j,i}\gls{sym:legendre}_{\gls{sym:geo_n},\gls{sym:geo_m}}\right] \label{eq:sigmaLam}
\end{align}
The expected value for a univariate continuous random variable can be computed as:
\begin{equation}
 \gls{sym:expVal}\left[\gls{sym:Z}\right] = \int_{-\infty}^{\infty} \gls{sym:zg} \gls{sym:probDens}\left(\gls{sym:zg}\right) d\gls{sym:zg}.
\end{equation}
For a unity sphere, points can be distributed uniformly so that for the surface area of $\gls{sym:area}=4\gls{sym:pi}$ the probability density function is given
as:
\begin{equation}
 \gls{sym:probDens}\left(\gls{sym:Z}\right) = \gls{sym:probDens}\left(\gls{sym:r}, \phigc, \gls{sym:long}\right) = \frac{1}{4\gls{sym:pi}}.
\end{equation}
The expected value in spherical coordinates for a given radial distance \gls{sym:r} is then
\begin{equation}
 \gls{sym:expVal}\left[\gls{sym:Z}\left(\gls{sym:r},\phigc,\gls{sym:long}\right)\right] =
\frac{1}{4\gls{sym:pi}}\int_{-\gls{sym:pi}/2}^{\gls{sym:pi}/2}\left(\int_{0}^{2\gls{sym:pi}}
\gls{sym:Z}\left(\gls{sym:r},\phigc,\gls{sym:long}\right)d\gls{sym:long}\right)\cos\phigc d\phigc. \label{eq:expval-z}
\end{equation}

\subsubsection{Integral evaluation for longitude functions}

In \eq{eq:expval-z} the inner integral is with respect to the longitude, which results in evaluating
\begin{align}
 \int_{0}^{2\gls{sym:pi}} \fnk{\gls{sym:geo_n}}{\gls{sym:geo_m}}\left(\gls{sym:long}\right) \fnk{j}{i}\left(\gls{sym:long}\right) d\gls{sym:long} &=
  \frac{1}{2}\Delta\cnm{}{}\Delta\cij{}{}
\int_{0}^{2\gls{sym:pi}}\left(\cos\left(\gls{sym:geo_m}-i\right)\gls{sym:long}+\cos\left(\gls{sym:geo_m}+i\right)\gls{sym:long}\right) d\gls{sym:long} + \notag \\
 &+ \frac{1}{2}\Delta\snm{}{}\Delta\sij{}{}
\int_{0}^{2\gls{sym:pi}}\left(\cos\left(\gls{sym:geo_m}-i\right)\gls{sym:long}-\cos\left(\gls{sym:geo_m}+i\right)\gls{sym:long}\right) d\gls{sym:long} +  \\
 &+ \frac{1}{2}\left(\Delta\cnm{}{}\Delta\sij{}{}+\Delta\snm{}{}\Delta\cij{}{}\right)
\int_{0}^{2\gls{sym:pi}}\left(\sin\left(\gls{sym:geo_m}-i\right)\gls{sym:long}+\sin\left(\gls{sym:geo_m}+i\right)\gls{sym:long}\right) d\gls{sym:long}. \notag
\end{align}
The above integrals are all zero for $\gls{sym:geo_m} \neq i$. If $\gls{sym:geo_m}=i \neq 0$, only the cosine functions with arguments
$\left(\gls{sym:geo_m}-i\right)\gls{sym:long}$ provide non-zero contributions, while for $\gls{sym:geo_m}=i=0$ only zonal harmonics remain (as
$\Delta\gls{sym:geo_S}_{\gls{sym:geo_n},0}= \Delta\gls{sym:geo_S}_{j,0} =0$). Therefore, the result of the integral evaluation is
\begin{equation}
 \int_{0}^{2\gls{sym:pi}} \fnk{\gls{sym:geo_n}}{\gls{sym:geo_m}}\left(\gls{sym:long}\right) \fnk{j}{i}\left(\gls{sym:long}\right) d\gls{sym:long}
 = \begin{cases}
   0 & \mbox{if } \gls{sym:geo_m} \neq i, \\
   \frac{2\gls{sym:pi}}{\gls{sym:deltam}}\left(\Delta\cnm{}{}\Delta\cij{}{}+\Delta\snm{}{}\Delta\sij{}{}\right) & \mbox{if } \gls{sym:geo_m} = i,
   \end{cases} \label{eq:integral-ff}
\end{equation}
where \gls{sym:deltam} is according to the definition in \eq{eq:normalization}. The same result is obtained for the derivatives functions:
\begin{equation}
 \int_{0}^{2\gls{sym:pi}} \gnk{\gls{sym:geo_n}}{\gls{sym:geo_m}}\left(\gls{sym:long}\right) \gnk{j}{i}\left(\gls{sym:long}\right) d\gls{sym:long}
 = \begin{cases}
   0 & \mbox{if } \gls{sym:geo_m} \neq i, \\
   \frac{2\gls{sym:pi}}{\gls{sym:deltam}}\left(\Delta\cnm{}{}\Delta\cij{}{}+\Delta\snm{}{}\Delta\sij{}{}\right) & \mbox{if } \gls{sym:geo_m} = i,
   \end{cases}
\end{equation}

\subsubsection{Integral evaluation for Legendre functions}

The outer integral in \eq{eq:expval-z} results in evaluating three different integrals for the combined Legendre functions in \eq{eq:sigmaRad} through \eq{eq:sigmaLam}.
Using the known orthogonality relations for the Legendre polynomials (e.g. \citet{abramowitz1964}), the first integral is easily obtained as:
\begin{align}
 \int_{-\gls{sym:pi}/2}^{\gls{sym:pi}/2} \gls{sym:legendre}_{\gls{sym:geo_n},\gls{sym:geo_m}}\left(\sin\phigc\right)
\gls{sym:legendre}_{j,i}\left(\sin\phigc\right) \cos\phigc d\phigc &= \int_{-1}^{1}
\gls{sym:legendre}_{\gls{sym:geo_n},\gls{sym:geo_m}}\left(\sin\phigc\right)
\gls{sym:legendre}_{j,i}\left(\sin\phigc\right) d\left(\sin\phigc\right) \notag \\
 &= \begin{cases}
     0 & \mbox{if } \gls{sym:geo_n} \neq j, \\
     \frac{2\left(\gls{sym:geo_n}+\gls{sym:geo_m}\right)!}{\left(2\gls{sym:geo_n}+1\right)\left(\gls{sym:geo_n}-\gls{sym:geo_m}\right)!} & \mbox{if } \gls{sym:geo_n} = j.
    \end{cases} \label{eq:legendre-orth-1}
\end{align}
The other two integrals involve a lengthy process to obtain the final result, which is given in full detail in the Appendix of \citet{nazarenko2010}:
\begin{align}
 \int_{-1}^{1} \gls{sym:legendre}_{\gls{sym:geo_n},\gls{sym:geo_m}}\left(\sin\phigc\right)
\gls{sym:legendre}_{j,i}\left(\sin\phigc\right) \frac{1}{\cos^2\phigc} d\left(\sin\phigc\right) &=
    \begin{cases}
      0 & \mbox{if } \gls{sym:geo_n} \neq j, \\
     \frac{\left(\gls{sym:geo_n}+\gls{sym:geo_m}\right)!}{\gls{sym:geo_m}\left(\gls{sym:geo_n}-\gls{sym:geo_m}\right)!} & \mbox{if } \gls{sym:geo_n} = j.
    \end{cases}     \\
 \int_{-1}^{1} \frac{\partial \gls{sym:legendre}_{\gls{sym:geo_n},\gls{sym:geo_m}}\left(\sin\phigc\right)}{\partial \phigc}
\frac{\partial \gls{sym:legendre}_{j,i}\left(\sin\phigc\right)}{\partial \phigc} d\left(\sin\phigc\right) &=
    \begin{cases}
      0 & \mbox{if } \gls{sym:geo_n} \neq j, \\
\frac{\left(\gls{sym:geo_n}+\gls{sym:geo_m}\right)!}{\left(\gls{sym:geo_n}-\gls{sym:geo_m}\right)!}\left(\frac{2\gls{sym:geo_n}\left(\gls{sym:geo_n}+1\right)}{2
\gls{sym:geo_n}+1}-\gls{sym:geo_m} \right) & \mbox{if } \gls{sym:geo_n} = j.
    \end{cases}
\end{align}

\subsubsection{Force model variances in spherical coordinates}

With the integrals being evaluated, the result for the variances, as introduced in \eq{eq:sigmaRad} through \eq{eq:sigmaLam}, can finally be written for the spherical
coordinate system. For the radial variance, by using the results from \eq{eq:integral-ff} and \eq{eq:legendre-orth-1} one obtains:
\begin{align}
 \gls{sym:stdev}_{\gls{idx:radial}}^2\left(\gls{sym:r}\right) = \frac{1}{4\gls{sym:pi}} \sum_{\gls{sym:geo_n}=2}^{\infty}
 \rhon{\gls{sym:geo_n}}^2\left(\gls{sym:geo_n}+1\right)^2\sum_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}}
\frac{2\left(\gls{sym:geo_n}+\gls{sym:geo_m}\right)!}{\gls{sym:deltam}\left(2\gls{sym:geo_n}+1\right)\left(\gls{sym:geo_n}-\gls{sym:geo_m}\right)!}2\gls{sym:pi}
\left(\Delta\cnm{}{}^2+\Delta\snm{}{}^2\right),
\end{align}
which simplifies as soon as normalized coefficients are considered using \eq{eq:normalization}:
\begin{align}
 \gls{sym:stdev}_{\gls{idx:radial}}^2\left(\gls{sym:r}\right) = \left(\frac{\gls{sym:grav}}{\gls{sym:r}^2}\right)^2 \sum_{\gls{sym:geo_n}=2}^{\infty}
 \left(\frac{\re}{\gls{sym:r}}\right)^{2\gls{sym:geo_n}}\left(\gls{sym:geo_n}+1\right)^2\sum_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}}
\left(\Delta\ncnm{}{}^2+\Delta\nsnm{}{}^2\right). \label{eq:stdev-rad}
\end{align}
For the variances in longitude and latitude direction, the derivation is analogously:
\begin{align}
 \gls{sym:stdev}_{\gls{idx:lat}}^2\left(\gls{sym:r}\right) &= \frac{1}{2}\left(\frac{\gls{sym:grav}}{\gls{sym:r}^2}\right)^2 \sum_{\gls{sym:geo_n}=2}^{\infty}
 \left(\frac{\re}{\gls{sym:r}}\right)^{2\gls{sym:geo_n}}\left(2\gls{sym:geo_n}+1\right)\sum_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}}
\left(\frac{2\gls{sym:geo_n}\left(\gls{sym:geo_n}+1\right)}{2\gls{sym:geo_n}+1}-\gls{sym:geo_m}\right)\left(\Delta\ncnm{}{}^2+\Delta\nsnm{}{}^2\right). \\
 \gls{sym:stdev}_{\gls{idx:lon}}^2\left(\gls{sym:r}\right) &= \frac{1}{2}\left(\frac{\gls{sym:grav}}{\gls{sym:r}^2}\right)^2 \sum_{\gls{sym:geo_n}=2}^{\infty}
 \left(\frac{\re}{\gls{sym:r}}\right)^{2\gls{sym:geo_n}}\left(2\gls{sym:geo_n}+1\right)\sum_{\gls{sym:geo_m}=0}^{\gls{sym:geo_n}} \gls{sym:geo_m}
\left(\Delta\ncnm{}{}^2+\Delta\nsnm{}{}^2\right).\label{eq:stdev-lon}
\end{align}
The individual variances are a function of the geocentric radius. For a selected geopotential model, the harmonic coefficients and their standard deviations are required
for the evaluation. According to the definition of the quantities $\Delta\ncnm{}{}$ and $\Delta\nsnm{}{}$ in \eq{eq:deltaCS}, the standard
deviations of the harmonic coefficients are summed up to the maximum degree of the geopotential considered in the propagation
($\gls{sym:geo_n}\leq\gls{sym:geo_n}_{max}$), while the harmonic coefficients are used for $\gls{sym:geo_n} > \gls{sym:geo_n}_{max}$. As \citet{nazarenko2010} points
out, the computation of the series in \eq{eq:stdev-rad} through \eq{eq:stdev-lon} can be carried out in two ways:
\begin{itemize}
 \item For $\gls{sym:geo_n}_{max}$ being small, the series will be truncated early, so that the error due to omission will be dominated by larger terms, with the
influence of terms of increasing order quickly declining. This means that the series with respect to the geopotential degree would be evaluated up to $\gls{sym:geo_n}
\approx 30\ldots40$.
 \item For the geopotential being considered to a high degree for high precise computations, the truncation sets in late and the amount of terms that are non-negligible
will be higher than in the case above. In order to reduce computational burden, \citet{nazarenko2010} recommends to use Kaula's rule \citep{kaula2000} for an efficient
evaluation (see \eq{eq:kaula}).
\end{itemize}
The second moment of process noise, which is required to evaluate \eq{eq:quu}, can be written in spherical coordinates as
\begin{equation}
 \gls{sym:expVal}\left[\gls{sym:inputVec}\gls{sym:inputVec}^T\right] = \gls{sym:expVal}\left[\begin{matrix}
                                                       \Delta \gls{sym:specForce}_{\gls{idx:radial}}^2 & \Delta \gls{sym:specForce}_{\gls{idx:radial}} \Delta
\gls{sym:specForce}_{\gls{idx:lat}} & \Delta \gls{sym:specForce}_{\gls{idx:radial}} \Delta \gls{sym:specForce}_{\gls{idx:lon}} \\
\Delta \gls{sym:specForce}_{\gls{idx:lat}} \Delta \gls{sym:specForce}_{\gls{idx:radial}} & \Delta \gls{sym:specForce}_{\gls{idx:lat}}^2 & \Delta
\gls{sym:specForce}_{\gls{idx:lat}} \Delta \gls{sym:specForce}_{\gls{idx:lon}} \\
\Delta \gls{sym:specForce}_{\gls{idx:lon}} \Delta \gls{sym:specForce}_{\gls{idx:radial}} & \Delta \gls{sym:specForce}_{\gls{idx:lon}} \Delta
\gls{sym:specForce}_{\gls{idx:lat}} & \Delta \gls{sym:specForce}_{\gls{idx:lon}}^2
\end{matrix}
\right]= \left(\begin{matrix}
                 \gls{sym:stdev}_{\gls{idx:radial}}^2 & 0 & 0 \\
                 0 & \gls{sym:stdev}_{\gls{idx:lat}}^2 & 0 \\
                 0 & 0 & \gls{sym:stdev}_{\gls{idx:lon}}^2
                \end{matrix}
\right),
\end{equation}
where the non-diagonal terms are derived in a similar manner. As pointed out by \citet{nazarenko2010}, they are all zero.

TBC.

\begin{figure}[h!]
  \centering
  \includegraphics[width=\textwidth]{auto-correlation.png}
  \caption{Normalized auto-correlation function for the EIGEN-GL04C geopotential as a function of angular separation and the orbit altitude being the parameter. \label{fig:auto-correlation}}
\end{figure}
\todo[Replace figure]{Replace \fig{fig:auto-correlation} using Tikz}

\subsection{Exemplary results}

\begin{figure}[h!]
  \centering
  \includegraphics[width=\textwidth]{correlation-leo.png}
  \caption{Normalized auto-correlation function for two \gls{acr:leo} orbits, showing also the correlation of the current with subsequent orbits.\label{fig:correlation-leo}}
\end{figure}
\todo[Replace figure]{Replace \fig{fig:correlation-leo} using Tikz}

\begin{figure}[h!]
  \centering
  \includegraphics[width=\textwidth]{correlation-gps.png}
  \caption{Normalized auto-correlation function for \gls{acr:gps} orbits, showing also the correlation of the current with subsequent orbits. A typical picture for \SI{12}{\hour} resonance orbits.\label{fig:correlation-gps}}
\end{figure}
\todo[Replace figure]{Replace \fig{fig:correlation-gps} using Tikz}

\begin{figure}[h!]
  \centering
  \includegraphics[width=\textwidth]{noise-results-matQ.png}
  \caption{Force model error contributions using a 30 $\times$ 30 geopotential, for an orbit at \SI{300}{\kilo\metre} and an inclination of \ang{50;;}.  \label{fig:noise-contributions}}
\end{figure}
\todo[Replace figure]{Replace \fig{fig:noise-contributions} using Tikz}


\chapter{Applications IV:  PLT-Redex-Style Reducer}\label{reducerchapter}

Robby's grammar for extended version of $\lambda_v$, using PLT Redex syntax:

\schemedisplayspace
\begin{schemedisplay}
(define-language lambdav
  (e (e e ...) (if0 e e e) x v)
  (v (lambda (x ...) e) number +)
  (E (v ... E e ...) (if0 E e e) hole)
  (x (variable-except lambda + if0)))
\end{schemedisplay}

simimar in spirit to BNF

Robby's reduction rules:

\schemedisplayspace
\begin{schemedisplay}
(define red
  (reduction-relation
   lambdav
   (--> (in-hole E (+ number_1 number_2))
        (in-hole E ,(+ (term number_1) (term number_2)))
        "+")
   (--> (in-hole E (if0 0 e_1 e_2))
        (in-hole E e_1)
        "if0t")
   (--> (in-hole E (if0 number_1 e_1 e_2))
        (in-hole E e_2)
        "if0f"
        (side-condition (not (= 0 (term number_1)))))
   (--> (in-hole E ((lambda (x ..._1) e) v ..._1))
        (in-hole E (subst-n (x v) ... e))
        "betav")))
\end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define-metafunction lambdav
  subst-n : (x any) ... any -> any
  [(subst-n (x_1 any_1) (x_2 any_2) ... any_3)
   (subst x_1 any_1 (subst-n (x_2 any_2) ... any_3))]
  [(subst-n any_3) any_3])
\end{schemedisplay}

% \schemedisplayspace
% \begin{schemedisplay}
% (define-metafunction lambdav
%   subst : x any any -> any
%   ;; 1. x_1 bound, so don't continue in lambda body
%   [(subst x_1 any_1 (lambda (x_2 ... x_1 x_3 ...) any_2))
%    (lambda (x_2 ... x_1 x_3 ...) any_2)
%    (side-condition (not (member (term x_1) (term (x_2 ...)))))]
%   ;; 2. general purpose capture avoiding case
%   [(subst x_1 any_1 (lambda (x_2 ...) any_2))
%    ,(term-let ([(x_new ...)
%                 (variables-not-in (term (x_1 any_1 any_2))
%                                   (term (x_2 ...)))])
%               (term
%                (lambda (x_new ...)
%                  (subst x_1 any_1 (subst-vars (x_2 x_new) ... any_2)))))]
%   ;; 3. replace x_1 with e_1
%   [(subst x_1 any_1 x_1) any_1]
%   ;; 4. x_1 and x_2 are different, so don't replace
%   [(subst x_1 any_1 x_2) x_2]
%   ;; the last two cases cover all other expression forms
%   [(subst x_1 any_1 (any_2 ...))
%    ((subst x_1 any_1 any_2) ...)]
%   [(subst x_1 any_1 any_2) any_2])
% \end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define-metafunction lambdav
  subst-vars : (x any) ... any -> any
  [(subst-vars (x_1 any_1) x_1) any_1]
  [(subst-vars (x_1 any_1) (any_2 ...)) ((subst-vars (x_1 any_1) any_2) ...)]
  [(subst-vars (x_1 any_1) any_2) any_2]
  [(subst-vars (x_1 any_1) (x_2 any_2) ... any_3)
   (subst-vars (x_1 any_1) (subst-vars (x_2 any_2) ... any_3))]
  [(subst-vars any) any])
\end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
 (traces red
          (term
           ((lambda (n)
              (if0
               n
               1
               ((lambda (x) (x x))
                (lambda (x) (x x)))))
            (+ 2 2))))
\end{schemedisplay}

This expression reduces to omega, and therefore has no normal form.
PLT Redex is smart enough to reach a fixed point in this case.  This
is an example where tabling would really help.

% alphaKanren version of lam-v

% William E. Byrd
% 11 October 2007

% Translated from lam-v.ss, and the lam-v example from Robby Findler's PLT Redex website:

%   http://www.cs.uchicago.edu/~robby/plt-redex/

% Uses the 'substo' relation adapted from Cheney and Urban:

% $\alpha${Prolog}: {A} Logic Programming Language with Names, Binding and $\alpha$-Equivalence
% ICLP, 2004

% May wish to add sub1 back again, although this slows down the tests.
% The sub1 code is in the with-sub1.scm file in ~//iu/mk/alphamk/plt-redex/.

% May wish to rewrite using ulambda macro, with @a extension for
% introducing noms.

% Test 12r seems to diverge when replacing conda with conde in
% safe-fully-reduceo.  Does tabling solve this problem?
% (Unfortunately, must combine tabling with nominal
% unification/constraints.)  Disequality constraints should solve the
% problem.

% See if I can run Robby's 'triangle' test that uses the Y-combinator.
% Perhaps triangular unification is fast enough.

% Also, give these test for Ramana for benchmarking.

% Email I sent to Robby Findler, 12 October 2007

% Hi Robby!

% Last night I decided it would be fun to implement the lambda_v
% example on your website in alphaKanren.  alphaKanren extends
% miniKanren with nominal unification, which makes implementing capture-
% avoiding substitution very easy;  otherwise, the attached code is
% written in a pure subset of miniKanren, without cuts, var tests, or
% other non-logical operators.  I did use a single occurrence of the
% conda "committed choice" operator, but this use is guaranteed to be
% safe, and could easily be replaced by a disequality constraint.

% The code passes all of the tests in lam_v.ss, with the exception of
% the 'triangle' test that uses the Y-combinator.  I think that this
% test terminates, but it runs *very* slowly.  Unfortunately, nominal
% unification is expensive.

% Now for the fun stuff.  Since the reduction relation is a true
% relation, we can run it "backwards".  For example, here are 15
% expressions that reduce to the number 5:

% (test "backwards-1"

%   (run 15 (q)
%     (fully-reduceo q `(num ,(build-num 5))))

%   `((num (1 0 1))                                          ; 5
%     (+ (num (1 0 1)) (num ()))                      ; (+ 5 0)
%     (+ (num ()) (num (1 0 1)))                      ;  (+ 0 5)
%     (+ (num (1)) (num (0 0 1)))                    ; (+ 1 4)
%     (if-tag (num ()) (num (1 0 1)) (var-tag _.0))    ; (if-tag 0 5 x), for any
% variable x
%     (+ (num (0 0 1)) (num (1)))                    ; (+ 4 1)
%     (if-tag (num ()) (num (1 0 1)) (num _.0))   ; (if-tag 0 5 n), for any
% number n
%     (app (lam (tie-tag x.0 (var-tag x.0))) (num (1 0 1)))   ; ((lambda x
% x) 5)
%     (if-tag (num (_.0 . _.1)) (var-tag _.2) (num (1 0 1)))       ; (if-tag n x
% 5), for any positive number n and any variable x
%     (+ (num (1 1)) (num (0 1)))                                    ;
% (+ 3 2)
%     (app (lam (tie-tag x.0 (var-tag x.0))) (+ (num (1 0 1)) (num
% ())))   ; ((lambda x x) (+ 5 0))
%     (app (lam (tie-tag x.0 (num (1 0 1)))) (num _.0))   ; ((lambda x
% 5) n),  for any number n
%     (if-tag (num (_.0 . _.1)) (num _.2) (num (1 0 1)))        ; (if-tag n
% m 5), for any positive number n, and any number m
%     (if-tag (num ()) (num (1 0 1)) (lam (tie-tag x.0 (var-tag _.0))))   ;
% (if-tag 0 5 (lambda (x) y)) for any variable y
%     (+ (num (0 1)) (num (1 1)))))       ; (+ 2 3)

% Anyway, I thought you might get a kick out of the code.  I've only
% tested this implementation under Chez Scheme--once again, some of the
% tests run very slowly, even with all of Kent's optimizations turned on.

% See you tomorrow!

% Cheers,

% --Will

% Email to Dan, the following day.

% The version I sent Robby has a single conda, which abides by the
% G-rule, and is therefore provably safe.  Also, this single conda
% could be replaced by a disequality constraint.  Furthermore, the
% conda is only used to detect when the reduced term is equal to the
% previous term, in which case the reducer terminates instead of
% diverging.

% --Will

% Email from Dan

% Will,

% Here's a question for you.  The great advantage of redex is
% that you can use it interactively.  How much effort would it
% take to do that.  So, we get one-step and fully, but suppose
% after one-step, I want to continue with another step.  Would
% that be tricky because we always reify?  Would we need an
% "unreify".  That should be easy to write.  Or can we just get
% by returning a data structure that allows for communication
% back to the stream of answers.

% My "Neat" was "Hey that's neat that you could do it; not
% wow, you did that in a neat way.  Now, that I have studied (read) the
% code, I would be willing to ask Robby (or Matthias) if they would
% like a contribution based on this for their book if you want me to.
% It may be too late, but it would be the best thing in their book :).

% Doing what you did IS exactly why I entered this phase of my
% research.  I wanted to be able to take "The rules" and just
% run them, and that is exactly what you did.

% Kudos, but I wonder how much we owe to the |alpha| folks.
% Writing this up is worth the time.  "Running \-v backwards using
% \ak", but right now, I am anxious to see the final version of
% @o.

% I wonder if there is not some better way to do fix than the Y
% combinator.  Harper's way (from his notes) might be better.

% ... Dan

% (load "alphamk.scm")

% (define-syntax test
%   (syntax-rules ()
%     ((_ title tested-expression expected-result)
%      (let* ((expected expected-result)
%             (produced tested-expression))
%        (if-tag (equal? expected produced)
%            (printf "~s works!\n" title)
%            (error
%             'test
%             "Failed ~s: ~a\nExpected: ~a\nComputed: ~a"
%             title 'tested-expression expected produced))))))

% (define-syntax test-divergence
%   (syntax-rules ()
%     ((_ title tested-expression)
%      (let ((max-ticks 1000000))
%        (printf "Testing ~s (engine with ~s ticks fuel)\n" title max-ticks)
%        ((make-engine (lambda () tested-expression))
%         max-ticks
%         (lambda (t v)
% 	  (error title "infinite loop returned ~s after ~s ticks" v (- max-ticks t)))
%         (lambda (e^) (void)))))))

\schemedisplayspace
\begin{schemedisplay}
(define valueo
  (lambda (v)
    (conde
      ((exist (n)
         (== `(num ,n) v)))
      ((exist (e)
         (fresh (x)
           (== `(lam ,(tie x e)) v)
           (expressiono e)))))))
\end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define expressiono
  (lambda (e)
    (conde
      ((valueo e))
      ((exist (x)
         (== `(var-tag ,x) e)))
      ((exist (e1 e2)
         (== `(app ,e1 ,e2) e)
         (expressiono e1)
         (expressiono e2)))
      ((exist (e1 e2)
         (== `(+ ,e1 ,e2) e)
         (expressiono e1)
         (expressiono e2)))
      ((exist (e1 e2 e3)
         (== `(if-tag ,e1 ,e2 ,e3) e)
         (expressiono e1)
         (expressiono e2)
         (expressiono e3))))))
\end{schemedisplay}

% \schemedisplayspace
% \begin{schemedisplay}
% '(define contexto
%   (lambda (t out)
%     (conde
%       ((in-holeo t out))
%       ((exist (C e v)
%          (== `(app ,C ,e) t)
%          (== `(app ,v ,e) out)
%          (expressiono e)
%          (contexto C v)))
%       ((exist (C v v^)
%          (== `(app ,v ,C) t)
%          (== `(app ,v ,v^) out)
%          (valueo v)
%          (contexto C v^)))
%       ((exist (C e v)
%          (== `(+ ,C ,e) t)
%          (== `(+ ,v ,e) out)
%          (expressiono e)
%          (contexto C v)))
%       ((exist (C v v^)
%          (== `(+ ,v ,C) t)
%          (== `(+ ,v ,v^) out)
%          (valueo v)
%          (contexto C v^)))
%       ((exist (C e1 e2 v)
%          (== `(if-tag ,C ,e1 ,e2) t)
%          (== `(if-tag ,v ,e1 ,e2) out)
%          (expressiono e1)
%          (expressiono e2)
%          (contexto C v))))))
% \end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define contexto
  (lambda (t out)
    (conde
      ((in-holeo t out))
      ((exist (C e v)
         (== `(app ,C ,e) t)
         (== `(app ,v ,e) out)
         (expressiono e)
         (contexto C v)))
      ((exist (C v v^)
         (== `(app ,v ,C) t)
         (== `(app ,v ,v^) out)
         (valueo v)
         (contexto C v^)))
      ((exist (C e v)
         (== `(+ ,C ,e) t)
         (== `(+ ,v ,e) out)
         (expressiono e)
         (contexto C v)))
      ((exist (C v v^)
         (== `(+ ,v ,C) t)
         (== `(+ ,v ,v^) out)
         (valueo v)
         (contexto C v^)))
      ((exist (C e1 e2 v)
         (== `(if-tag ,C ,e1 ,e2) t)
         (== `(if-tag ,v ,e1 ,e2) out)
         (expressiono e1)
         (expressiono e2)
         (contexto C v))))))
\end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define one-stepo
  (lambda (t out)
    (contexto t out)))
\end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define fully-reduceo
  (lambda (t out)
    (exist (res)
      (conde
        ((contexto t res)
         (fully-reduceo res out))
        ((== t out) (valueo t))))))
\end{schemedisplay}

% \schemedisplayspace
% \begin{schemedisplay}
% '(define safe-fully-reduceo
%   (lambda (t out)
%     (exist (res)
%       (conde
%         ((contexto t res)
%          ; This nested conda could be replaced with a disequality
%          ; constraint.  This conda is safe, however, since t and res
%          ; do not become more instantiated in the conda test.
%          ; (Actually, is this true?  I'm not sure of this, since fresh
%          ; variables in t could become associated with fresh variables
%          ; in res.  I could try to verify this claim.  But better to
%          ; use =/= or tabling.)
%          (conda
%            ((== t res) (== res out))
%            ((== #t #t) (safe-fully-reduceo res out))))
%         ((== t out) (valueo t))))))
% \end{schemedisplay}

% \schemedisplayspace
% \begin{schemedisplay}
% '(define safe-fully-reduceo
%   (lambda (t out)
%     (exist (res)
%       (conde
%         ((contexto t res)
%          (conde
%            ((== t res) (== res out))
%            ; If we added disequality constraints to alphaKanren, we
%            ; could write this.
%            ((=/= t res) (safe-fully-reduceo res out))))
%         ((== t out) (valueo t))))))
% \end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define safe-fully-reduceo
  (lambda (t out)
    (exist (res)
      (conde
        ((contexto t res)
         (conde
           ((== t res) (== res out))
           ; I *think* tabling would be sufficient to prevent
           ; divergence and duplicate answers.
           ((safe-fully-reduceo res out))))
        ((== t out) (valueo t))))))
\end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define in-holeo
  (lambda (t out)
    (conde
      ((exist (n1 n2 sum)
         (== `(+ (num ,n1) (num ,n2)) t)
         (== `(num ,sum) out)
         (pluso n1 n2 sum)))
      ((exist (e1 e2)
         (== `(if-tag (num ,(build-num 0)) ,e1 ,e2) t)
         (== e1 out)
         (expressiono e1)
         (expressiono e2)))
      ((exist (n e1 e2)
         (== `(if-tag (num ,n) ,e1 ,e2) t)
         (== e2 out)
         (poso n)
         (expressiono e1)
         (expressiono e2)))
      ((exist (e v s)
         (fresh (x)
           (== `(app (lam ,(tie x e)) ,v) t)
           (valueo v)
           (expressiono e)
           (substo e v x out)))))))
\end{schemedisplay}

\schemedisplayspace
\begin{schemedisplay}
(define substo
  (lambda (e new a out)
    (conde
      ((== `(var-tag ,a) e) (== new out))
      ((exist (y)
         (== `(var-tag ,y) e)
         (== `(var-tag ,y) out)
         (hash a y)))
      ((exist (n)
         (== `(num ,n) e)
         (== `(num ,n) out)))
      ((exist (rator ratorres rand randres)
         (== `(app ,rator ,rand) e)
         (== `(app ,ratorres ,randres) out)
         (substo rator new a ratorres)
         (substo rand new a randres)))
      ((exist (e1 e1res e2 e2res)
         (== `(+ ,e1 ,e2) e)
         (== `(+ ,e1res ,e2res) out)
         (substo e1 new a e1res)
         (substo e2 new a e2res)))
      ((exist (e1 e1res e2 e2res e3 e3res)
         (== `(if-tag ,e1 ,e2 ,e3) e)
         (== `(if-tag ,e1res ,e2res ,e3res) out)
         (substo e1 new a e1res)
         (substo e2 new a e2res)
         (substo e3 new a e3res)))
      ((exist (body bodyres)
         (fresh (c)
           (== `(lam ,(tie c body)) e)
           (== `(lam ,(tie c bodyres)) out)
           (hash c a)
           (hash c new)
           (substo body new a bodyres)))))))
\end{schemedisplay}

% ;;; Standard relational arithmetic helpers.

% (define build-num
%   (lambda (n)
%     (cond
%       ((zero? n) '())
%       ((and (not (zero? n)) (even? n))
%        (cons 0
%          (build-num (quotient n 2))))
%       ((odd? n)
%        (cons 1
%          (build-num (quotient (- n 1) 2)))))))

% (define poso
%   (lambda (n)
%     (exist (a d)
%       (== `(,a . ,d) n))))

% (define >1o
%   (lambda (n)
%     (exist (a ad dd)
%       (== `(,a ,ad . ,dd) n))))

% (define pluso
%   (lambda (n m k)
%     (addero 0 n m k)))

% (define addero
%   (lambda (d n m r)
%     (conde
%       ((== 0 d) (== '() m) (== n r))
%       ((== 0 d) (== '() n) (== m r)
%        (poso m))
%       ((== 1 d) (== '() m)
%        (addero 0 n '(1) r))
%       ((== 1 d) (== '() n) (poso m)
%        (addero 0 '(1) m r))
%       ((== '(1) n) (== '(1) m)
%        (exist (a c)
%          (== `(,a ,c) r)
%          (full-addero d 1 1 a c)))
%       ((== '(1) n) (gen-addero d n m r))
%       ((== '(1) m) (>1o n) (>1o r)
%        (addero d '(1) n r))
%       ((>1o n) (gen-addero d n m r)))))

% (define full-addero
%   (lambda (b x y r c)
%     (conde
%       ((== 0 b) (== 0 x) (== 0 y) (== 0 r) (== 0 c))
%       ((== 1 b) (== 0 x) (== 0 y) (== 1 r) (== 0 c))
%       ((== 0 b) (== 1 x) (== 0 y) (== 1 r) (== 0 c))
%       ((== 1 b) (== 1 x) (== 0 y) (== 0 r) (== 1 c))
%       ((== 0 b) (== 0 x) (== 1 y) (== 1 r) (== 0 c))
%       ((== 1 b) (== 0 x) (== 1 y) (== 0 r) (== 1 c))
%       ((== 0 b) (== 1 x) (== 1 y) (== 0 r) (== 1 c))
%       ((== 1 b) (== 1 x) (== 1 y) (== 1 r) (== 1 c)))))

% (define gen-addero
%   (lambda (d n m r)
%     (exist (a b c e x y z)
%       (== `(,a . ,x) n)
%       (== `(,b . ,y) m) (poso y)
%       (== `(,c . ,z) r) (poso z)
%       (full-addero d a b c e)
%       (addero e x y z))))

% ;;; Diverges when using conde instead of conda in safe-fully-reduceo.
% ;;; (Since this is omega!)
% ;;; Should converge with =/=, and I think would converge with tabling.
% (test "12r"
%   (run* (q)
%     (fresh (x)
%       (safe-fully-reduceo `(app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                                 (lam ,(tie x `(app (var-tag ,x) (var-tag ,x)))))
%                           q)))
%   '((app (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0)))) (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0)))))))
% #!eof

% ;;; Tests

% (test "1"
%   ; Can't reduce a number.
%   (run* (q)
%     (one-stepo `(num ,(build-num 5)) q))
%   '())

% (test "2"
%   (run* (q)
%     (one-stepo `(+ (num ,(build-num 5)) (num ,(build-num 3))) q))
%   '((num (0 0 0 1))))

% (test "3"
%   (run* (q)
%     (one-stepo `(if-tag (num ,(build-num 3)) (num ,(build-num 4)) (num ,(build-num 5))) q))
%   '((num (1 0 1))))

% (test "4"
%   (run* (q)
%     (one-stepo `(if-tag (num ,(build-num 0)) (num ,(build-num 4)) (num ,(build-num 5))) q))
%   '((num (0 0 1))))

% (test "5"
%   (run* (q)
%     (one-stepo `(if-tag (+ (num ,(build-num 5)) (num ,(build-num 3)))
%                      (num ,(build-num 4))
%                      (num ,(build-num 5))) q))
%   '((if-tag (num (0 0 0 1)) (num (0 0 1)) (num (1 0 1)))))

% (test "6"
%   (run* (q)
%     (one-stepo `(if-tag (+ (num ,(build-num 0)) (num ,(build-num 0)))
%                      (num ,(build-num 4))
%                      (num ,(build-num 5))) q))
%   '((if-tag (num ()) (num (0 0 1)) (num (1 0 1)))))

% (test "1r"
%   (run* (q)
%     (fully-reduceo `(num ,(build-num 5)) q))
%   '((num (1 0 1))))

% (test "2r"
%   (run* (q)
%     (fully-reduceo `(+ (num ,(build-num 5)) (num ,(build-num 3))) q))
%   '((num (0 0 0 1))))

% (test "3r"
%   (run* (q)
%     (fully-reduceo `(if-tag (num ,(build-num 3)) (num ,(build-num 4)) (num ,(build-num 5))) q))
%   '((num (1 0 1))))

% (test "4r"
%   (run* (q)
%     (fully-reduceo `(if-tag (num ,(build-num 0)) (num ,(build-num 4)) (num ,(build-num 5))) q))
%   '((num (0 0 1))))

% (test "5r"
%   (run* (q)
%     (fully-reduceo `(if-tag (+ (num ,(build-num 5)) (num ,(build-num 3)))
%                          (num ,(build-num 4))
%                          (num ,(build-num 5))) q))
%   '((num (1 0 1))))

% (test "6r"
%   (run* (q)
%     (fully-reduceo `(if-tag (+ (num ,(build-num 0)) (num ,(build-num 0)))
%                          (num ,(build-num 4))
%                          (num ,(build-num 5))) q))
%   '((num (0 0 1))))

% (test "7r"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(lam ,(tie x `(num ,(build-num 3)))) q)))
%   '((lam (tie-tag x.0 (num (1 1))))))

% (test "8r"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(num ,(build-num 3)))) (num ,(build-num 4))) q)))
%   '((num (1 1))))

% (test "9r"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(var-tag ,x))) (num ,(build-num 4))) q)))
%   '((num (0 0 1))))

% (test "10r"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(if-tag (var-tag ,x)
%                                               (num ,(build-num 3))
%                                               (num ,(build-num 4)))))
%                            (num ,(build-num 5)))
%                      q)))
%   `((num ,(build-num 4))))

% (test "substo-1"
%   (run* (q)
%     (fresh (x)
%       (substo `(if-tag (var-tag ,x)
%                     (num ,(build-num 3))
%                     (num ,(build-num 4)))
%               `(num ,(build-num 5))
%               x
%               q)))
%   `((if-tag (num ,(build-num 5))
%          (num ,(build-num 3))
%          (num ,(build-num 4)))))

% (test "in-holeo-1"
%   (run* (q)
%     (fresh (x)
%       (in-holeo `(app (lam ,(tie x `(var-tag ,x))) (num ,(build-num 4))) q)))
%   `((num ,(build-num 4))))

% (test "11r"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(if-tag (var-tag ,x)
%                                               (num ,(build-num 3))
%                                               (num ,(build-num 4)))))
%                            (num ,(build-num 0)))
%                      q)))
%   `((num ,(build-num 3))))

% ;;; Diverges when using conde instead of conda in safe-fully-reduceo.
% ;;; (Since this is omega!)
% ;;; Should converge with =/=, and I think would converge with tabling.
% (test "12r"
%   (run* (q)
%     (fresh (x)
%       (safe-fully-reduceo `(app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                                 (lam ,(tie x `(app (var-tag ,x) (var-tag ,x)))))
%                           q)))
%   '((app (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0)))) (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0)))))))

% (test "13r"
%   (run* (q)
%     (fresh (x y)
%       (one-stepo `(app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                        (lam ,(tie y `(app (var-tag ,y) (var-tag ,y)))))
%                  q)))
%   '((app (lam (tie-tag y.0 (app (var-tag y.0) (var-tag y.0)))) (lam (tie-tag y.0 (app (var-tag y.0) (var-tag y.0)))))))

% (test "14r"
%   (run* (q)
%     (fresh (x)
%       (one-stepo `(app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                        (lam ,(tie x `(app (var-tag ,x) (var-tag ,x)))))
%                  q)))
%   '((app (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0)))) (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0)))))))

% (test-divergence "15r"
%   (run 1 (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                            (lam ,(tie x `(app (var-tag ,x) (var-tag ,x)))))
%                      q))))

% ; Robby's tests

% (test "Robby-1"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(var-tag ,x))) (num ,(build-num 1))) q)))
%   `((num ,(build-num 1))))

% (test "Robby-2"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (app (lam ,(tie x `(lam ,(tie x `(var-tag ,x)))))
%                                 (num ,(build-num 1)))
%                            (num ,(build-num 2)))
%                      q)))
%   `((num ,(build-num 2))))

% (test "Robby-3"
%   (run* (q)
%     (fresh (x y)
%       (fully-reduceo `(app (app (lam ,(tie x `(lam ,(tie y `(var-tag ,x)))))
%                                 (num ,(build-num 1)))
%                            (num ,(build-num 2)))
%                      q)))
%   `((num ,(build-num 1))))

% (test "Robby-4"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(+ (var-tag ,x) (var-tag ,x))))
%                            (num ,(build-num 2)))
%                      q)))
%   `((num ,(build-num 4))))

% (test "Robby-5"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(if-tag (var-tag ,x)
%                                               (var-tag ,x)
%                                               (+ (var-tag ,x) (num ,(build-num 1))))))
%                            (num ,(build-num 2)))
%                      q)))
%   `((num ,(build-num 3))))

% (test "Robby-6"
%   (run* (q)
%     (fresh (x)
%       (fully-reduceo `(app (lam ,(tie x `(if-tag (var-tag ,x)
%                                               (var-tag ,x)
%                                               (+ (var-tag ,x) (num ,(build-num 1))))))
%                            (num ,(build-num 0)))
%                      q)))
%   `((num ,(build-num 0))))

% ;;; Doh!  This example uses negative numbers.
% ;;; We must add sub1 to the rules to test this example.
% ;;;
% ;;; Even with sub1 added, this test takes a *long* time to run
% ;;; (I assume it isn't diverging, since evaluating tri alone took several minutes on Casper.)
% '(test "Robby-tough"
%   (run* (q)
%     (fresh (le f x triangle)
%       (let ((tri
%              `(app (lam ,(tie le `(app (lam ,(tie f `(app (var-tag ,le)
%                                                           (lam ,(tie x `(app (app (var-tag ,f) (var-tag ,f)) (var-tag ,x)))))))
%                                        (lam ,(tie f `(app (var-tag ,le)
%                                                           (lam ,(tie x `(app (app (var-tag ,f) (var-tag ,f)) (var-tag ,x))))))))))
%                    (lam ,(tie triangle `(lam ,(tie x `(if-tag (var-tag ,x)
%                                                            (num ,(build-num 0))
%                                                            (+ (var-tag ,x) (app (var-tag ,triangle)
%                                                                             (sub1 (var-tag ,x))))))))))))
%         (one-stepo `(app ,tri (num ,(build-num 5))) q))))
%   `((num ,(build-num (+ 5 4 3 2 1 0)))))

% (test "Robby-8"
%   (run* (q)
%     (fresh (x y)
%       (substo `(var-tag ,x) `(var-tag ,y) x q)))
%   '((var-tag y.0)))

% (test "Robby-9"
%   (run* (q)
%     (fresh (x y z)
%       (substo `(var-tag ,z) `(var-tag ,y) x q)))
%   '((var-tag z.0)))

% (test "Robby-10"
%   (run* (q)
%     (fresh (x y z)
%       (substo `(app (var-tag ,x) (app (var-tag ,y) (var-tag ,z))) `(var-tag ,y) x q)))
%   '((app (var-tag y.0) (app (var-tag y.0) (var-tag z.0)))))

% (test "Robby-11"
%   (run* (q)
%     (fresh (x y y1 z)
%       (substo `(app (lam ,(tie x `(var-tag ,x)))
%                     (app (lam ,(tie y1 `(var-tag ,y1)))
%                          (lam ,(tie x `(var-tag ,z)))))
%               `(var-tag ,y) x q)))
%   '((app (lam (tie-tag c.0 (var-tag c.0))) (app (lam (tie-tag c.1 (var-tag c.1))) (lam (tie-tag c.2 (var-tag z.0)))))))

% (test "Robby-12"
%   (run* (q)
%     (fresh (x y)
%       (substo `(if-tag (+ (num ,(build-num 1)) (var-tag ,x)) (var-tag ,x) (var-tag ,x))
%               `(var-tag ,y) x q)))
%   `((if-tag (+ (num ,(build-num 1)) (var-tag y.0)) (var-tag y.0) (var-tag y.0))))

% (test "Robby-13"
%   (run* (q)
%     (fresh (x y z)
%       (substo `(lam ,(tie y `(var-tag ,x)))
%               `(lam ,(tie z `(var-tag ,y))) x q)))
%   `((lam (tie-tag c.0 (lam (tie-tag z.0 (var-tag y.0)))))))

% (test "Robby-14"
%   (run* (q)
%     (fresh (x y z)
%       (substo `(lam ,(tie y `(var-tag ,x)))
%               `(num ,(build-num 1)) x q)))
%   `((lam (tie-tag c.0 (num ,(build-num 1))))))

% (test "Robby-15"
%   (run* (q)
%     (fresh (x y z)
%       (substo `(lam ,(tie y `(var-tag ,x)))
%               `(var-tag ,y) x q)))
%   `((lam (tie-tag c.0 (var-tag y.0)))))

% (test "Robby-16"
%   (run* (q)
%     (fresh (x y z z2)
%       (substo `(lam ,(tie z `(app (var-tag ,z2) (var-tag ,z))))
%               `(lam ,(tie y `(var-tag ,y))) x q)))
%   `((lam (tie-tag c.0 (app (var-tag z2.0) (var-tag c.0))))))

% (test "Robby-17"
%   (run* (q)
%     (fresh (x z z1)
%       (substo `(lam ,(tie z `(app (var-tag ,z1) (var-tag ,z))))
%               `(lam ,(tie z `(var-tag ,z))) x q)))
%   `((lam (tie-tag c.0 (app (var-tag z1.0) (var-tag c.0))))))

% (test "Robby-18"
%   (run* (q)
%     (fresh (x z z1)
%       (substo `(lam ,(tie z `(app (var-tag ,z1) (var-tag ,z))))
%               `(var-tag ,z) x q)))
%   `((lam (tie-tag c.0 (app (var-tag z1.0) (var-tag c.0))))))

% (test "Robby-19"
%   (run* (q)
%     (fresh (x3 x2)
%       (substo `(lam ,(tie x2 `(var-tag ,x2)))
%               `(num ,(build-num 5)) x3 q)))
%   `((lam (tie-tag c.0 (var-tag c.0)))))

% ;;; Robby's main example

% (test "more-Robby-1"
%   (run* (q)
%     (fresh (n x)
%       (one-stepo `(app (lam ,(tie n `(if
%                                        (var-tag ,n)
%                                        (num ,(build-num 1))
%                                        (app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                                             (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))))))
%                        (+ (num ,(build-num 2)) (num ,(build-num 2))))
%                  q)))
%   '((app (lam (tie-tag
%              n.0
%              (if-tag (var-tag n.0)
%                   (num (1))
%                   (app (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0))))
%                        (lam (tie-tag x.0 (app (var-tag x.0) (var-tag x.0))))))))
%       (num (0 0 1)))))

% (test "more-Robby-2"
%   (run 1 (q)
%     (fresh (n x)
%       (safe-fully-reduceo `(app (lam ,(tie n `(if
%                                                 (var-tag ,n)
%                                                 (num ,(build-num 1))
%                                                 (app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                                                      (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))))))
%                                 (+ (num ,(build-num 2)) (num ,(build-num 2))))
%                           q)))
%   '((app (lam (tie-tag c.0 (app (var-tag c.0) (var-tag c.0)))) (lam (tie-tag c.0 (app (var-tag c.0) (var-tag c.0)))))))

% (test "more-Robby-3"
%   (run* (q)
%     (fresh (n x)
%       (safe-fully-reduceo `(app (lam ,(tie n `(if
%                                                 (var-tag ,n)
%                                                 (num ,(build-num 1))
%                                                 (app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                                                      (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))))))
%                                 (+ (num ,(build-num 2)) (num ,(build-num 2))))
%                           q)))
%   '((app (lam (tie-tag c.0 (app (var-tag c.0) (var-tag c.0)))) (lam (tie-tag c.0 (app (var-tag c.0) (var-tag c.0)))))))

% (test-divergence "more-Robby-4"
%   (run 1 (q)
%     (fresh (n x)
%       (fully-reduceo `(app (lam ,(tie n `(if
%                                            (var-tag ,n)
%                                            (num ,(build-num 1))
%                                            (app (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))
%                                                 (lam ,(tie x `(app (var-tag ,x) (var-tag ,x))))))))
%                            (+ (num ,(build-num 2)) (num ,(build-num 2))))
%                      q))))

% (test "backwards-0"
%   (run 20 (q)
%     (one-stepo q `(num ,(build-num 5))))
%   `((+ (num (1 0 1)) (num ()))
%     (+ (num ()) (num (1 0 1)))
%     (+ (num (1)) (num (0 0 1)))
%     (if-tag (num ()) (num (1 0 1)) (var-tag _.0))
%     (if-tag (num ()) (num (1 0 1)) (num _.0))
%     (+ (num (0 0 1)) (num (1)))
%     (+ (num (1 1)) (num (0 1)))
%     (+ (num (0 1)) (num (1 1)))
%     (if-tag (num ()) (num (1 0 1)) (lam (tie-tag x.0 (var-tag _.0))))
%     (if-tag (num ()) (num (1 0 1)) (lam (tie-tag x.0 (num _.0))))
%     (app (lam (tie-tag x.0 (var-tag x.0))) (num (1 0 1)))
%     (if-tag (num (_.0 . _.1)) (var-tag _.2) (num (1 0 1)))
%     (if-tag (num (_.0 . _.1)) (num _.2) (num (1 0 1)))
%     (if-tag (num ()) (num (1 0 1)) (lam (tie-tag x.0 (lam (tie-tag x.1 (var-tag _.0))))))
%     (if-tag (num ()) (num (1 0 1)) (lam (tie-tag x.0 (lam (tie-tag x.1 (num _.0))))))
%     (app (lam (tie-tag x.0 (num (1 0 1)))) (num _.0))
%     (if-tag (num ()) (num (1 0 1)) (app (var-tag _.0) (var-tag _.1)))
%     (if-tag (num ()) (num (1 0 1)) (app (var-tag _.0) (num _.1)))
%     (if-tag (num (_.0 . _.1)) (lam (tie-tag x.0 (var-tag _.2))) (num (1 0 1)))
%     (if-tag (num ()) (num (1 0 1))
%          (lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (var-tag _.0))))))))))

% (test "generate-0"
%   (run 20 (q)
%     (exist (e v)
%       (one-stepo e v)
%       (== `(,e ,v) q)))
%   `(((+ (num _.0) (num ())) (num _.0))
%     ((+ (num ()) (num (_.0 . _.1))) (num (_.0 . _.1)))
%     ((+ (num (1)) (num (1))) (num (0 1)))
%     ((+ (num (1)) (num (0 _.0 . _.1))) (num (1 _.0 . _.1)))
%     ((app (+ (num _.0) (num ())) (var-tag _.1)) (app (num _.0) (var-tag _.1)))
%     ((if-tag (num ()) (var-tag _.0) (var-tag _.1)) (var-tag _.0))
%     ((if-tag (num ()) (var-tag _.0) (num _.1)) (var-tag _.0))
%     ((+ (num (1)) (num (1 1))) (num (0 0 1)))
%     ((app (+ (num _.0) (num ())) (num _.1)) (app (num _.0) (num _.1)))
%     ((app (num _.0) (+ (num _.1) (num ()))) (app (num _.0) (num _.1)))
%     ((app (+ (num ()) (num (_.0 . _.1))) (var-tag _.2)) (app (num (_.0 . _.1)) (var-tag _.2)))
%     ((if-tag (num ()) (num _.0) (var-tag _.1)) (num _.0))
%     ((+ (num (0 _.0 . _.1)) (num (1))) (num (1 _.0 . _.1)))
%     ((+ (num (1)) (num (1 0 _.0 . _.1))) (num (0 1 _.0 . _.1)))
%     ((+ (num (0 1)) (num (0 1))) (num (0 0 1)))
%     ((if-tag (num ()) (num _.0) (num _.1)) (num _.0))
%     ((app (lam (tie-tag x.0 (var-tag x.0))) (num _.0)) (num _.0))
%     ((if-tag (num (_.0 . _.1)) (var-tag _.2) (var-tag _.3)) (var-tag _.3))
%     ((+ (num (1)) (num (1 1 1))) (num (0 0 0 1)))
%     ((app (+ (num (1)) (num (1))) (var-tag _.0)) (app (num (0 1)) (var-tag _.0)))))

% (test "backwards-1"
%   (run 20 (q)
%     (fully-reduceo q `(num ,(build-num 5))))
%   `((num (1 0 1))
%     (+ (num (1 0 1)) (num ()))
%     (+ (num ()) (num (1 0 1)))
%     (+ (num (1)) (num (0 0 1)))
%     (if-tag (num ()) (num (1 0 1)) (var-tag _.0))
%     (+ (num (0 0 1)) (num (1)))
%     (if-tag (num ()) (num (1 0 1)) (num _.0))
%     (app (lam (tie-tag x.0 (var-tag x.0))) (num (1 0 1)))
%     (if-tag (num (_.0 . _.1)) (var-tag _.2) (num (1 0 1)))
%     (+ (num (1 1)) (num (0 1)))
%     (app (lam (tie-tag x.0 (var-tag x.0))) (+ (num (1 0 1)) (num ())))
%     (app (lam (tie-tag x.0 (num (1 0 1)))) (num _.0))
%     (if-tag (num (_.0 . _.1)) (num _.2) (num (1 0 1)))
%     (if-tag (num ()) (num (1 0 1)) (lam (tie-tag x.0 (var-tag _.0))))
%     (+ (num (0 1)) (num (1 1)))
%     (+ (+ (num (1 0 1)) (num ())) (num ()))
%     (+ (+ (num ()) (num ())) (num (1 0 1)))
%     (+ (num (1 0 1)) (+ (num ()) (num ())))
%     (+ (num ()) (+ (num (1 0 1)) (num ())))
%     (+ (+ (num (1)) (num ())) (num (0 0 1)))))

% (test "generate-1"
%   (run 20 (q)
%     (exist (e v)
%       (fully-reduceo e v)
%       (== `(,e ,v) q)))
%   `(((num _.0)
%      (num _.0))
%     ((lam (tie-tag x.0 (var-tag _.0)))
%      (lam (tie-tag x.0 (var-tag _.0))))
%     ((lam (tie-tag x.0 (num _.0)))
%      (lam (tie-tag x.0 (num _.0))))
%     ((lam (tie-tag x.0 (lam (tie-tag x.1 (var-tag _.0)))))
%      (lam (tie-tag x.0 (lam (tie-tag x.1 (var-tag _.0))))))
%     ((+ (num _.0) (num ()))
%      (num _.0))
%     ((lam (tie-tag x.0 (lam (tie-tag x.1 (num _.0)))))
%      (lam (tie-tag x.0 (lam (tie-tag x.1 (num _.0))))))
%     ((+ (num ()) (num (_.0 . _.1)))
%      (num (_.0 . _.1)))
%     ((lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (var-tag _.0)))))))
%      (lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (var-tag _.0))))))))
%     ((lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (num _.0)))))))
%      (lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (num _.0))))))))
%     ((lam (tie-tag x.0 (app (var-tag _.0) (var-tag _.1))))
%      (lam (tie-tag x.0 (app (var-tag _.0) (var-tag _.1)))))
%     ((+ (num (1)) (num (1)))
%      (num (0 1)))
%     ((lam (tie-tag x.0 (app (var-tag _.0) (num _.1))))
%      (lam (tie-tag x.0 (app (var-tag _.0) (num _.1)))))
%     ((lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (lam (tie-tag x.3 (var-tag _.0)))))))))
%      (lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (lam (tie-tag x.3 (var-tag _.0))))))))))
%     ((lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (lam (tie-tag x.3 (num _.0)))))))))
%      (lam (tie-tag x.0 (lam (tie-tag x.1 (lam (tie-tag x.2 (lam (tie-tag x.3 (num _.0))))))))))
%     ((lam (tie-tag x.0 (app (num _.0) (var-tag _.1))))
%      (lam (tie-tag x.0 (app (num _.0) (var-tag _.1)))))
%     ((lam (tie-tag x.0 (app (num _.0) (num _.1))))
%      (lam (tie-tag x.0 (app (num _.0) (num _.1)))))
%     ((lam (tie-tag x.0 (+ (var-tag _.0) (var-tag _.1))))
%      (lam (tie-tag x.0 (+ (var-tag _.0) (var-tag _.1)))))
%     ((lam (tie-tag x.0 (lam (tie-tag x.1 (app (var-tag _.0) (var-tag _.1))))))
%      (lam (tie-tag x.0 (lam (tie-tag x.1 (app (var-tag _.0) (var-tag _.1)))))))
%     ((lam (tie-tag x.0 (+ (var-tag _.0) (num _.1))))
%      (lam (tie-tag x.0 (+ (var-tag _.0) (num _.1)))))
%     ((+ (num (1)) (num (0 _.0 . _.1)))
%      (num (1 _.0 . _.1)))))

% (test "generate-2"
%   ; More interesting answers.
%   ; Only look at the applications.
%   (run 10 (q)
%     (exist (e v rator rand)
%       (== `(app ,rator ,rand) e)
%       (fully-reduceo e v)
%       (== `(,e ,v) q)))
%   '(((app (lam (tie-tag x.0 (var-tag x.0))) (num _.0)) (num _.0))
%     ((app (lam (tie-tag x.0 (num _.0))) (num _.1)) (num _.0))
%     ((app (lam (tie-tag x.0 (var-tag x.0)))
%           (lam (tie-tag x.1 (var-tag _.0))))
%      (lam (tie-tag x.1 (var-tag _.0))))
%     ((app (lam (tie-tag x.0 (num _.0)))
%           (lam (tie-tag x.1 (var-tag _.1))))
%      (num _.0))
%     (((app (lam (tie-tag x.0 (lam (tie-tag x.1 (var-tag x.0)))))
%            (num _.0))
%       (lam (tie-tag c.0 (num _.0)))) : ((c.0 . _.0)))
%     (((app (lam (tie-tag x.0 (lam (tie-tag x.1 (var-tag _.0)))))
%            (num _.1))
%       (lam (tie-tag c.0 (var-tag (susp-tag ((c.0 x.1)) _.0))))) : ((c.0 . _.0) (c.0 . _.1) (x.0 . _.0)))
%     (((app (lam (tie-tag x.0 (lam (tie-tag x.1 (num _.0)))))
%            (num _.1))
%       (lam (tie-tag c.0 (num (susp-tag ((c.0 x.1)) _.0))))) : ((c.0 . _.0) (c.0 . _.1)))
%     ((app (lam (tie-tag x.0 (var-tag x.0))) (+ (num _.0) (num ())))
%      (num _.0))
%     ((app (lam (tie-tag x.0 (var-tag x.0)))
%           (lam (tie-tag x.1 (num _.0))))
%      (lam (tie-tag x.1 (num _.0))))
%     ((app (lam (tie-tag x.0 (num _.0)))
%           (lam (tie-tag x.1 (num _.1))))
%      (num _.0))))

% (test "generate-3"
%   ; More interesting answers.
%   ; Only look at the applications.
%   ; Note that we perform the filtering *after* generating/reducing the expressions.
%   (run 6 (q)
%     (exist (e v rator rand)
%       (fully-reduceo e v)
%       (== `(app ,rator ,rand) e)
%       (== `(,e ,v) q)))
%   `(((app (lam (tie-tag x.0 (var-tag x.0))) (num _.0)) (num _.0))
%     ((app (lam (tie-tag x.0 (var-tag x.0))) (+ (num _.0) (num ())))
%      (num _.0))
%     ((app (lam (tie-tag x.0 (num _.0))) (num _.1)) (num _.0))
%     (((app (lam (tie-tag x.0 (num _.0))) (+ (num _.1) (num ())))
%       (num (susp-tag ((x.1 x.0)) _.0))) : ((x.1 . _.0)))
%     ((app (lam (tie-tag x.0 (var-tag x.0)))
%           (+ (num ()) (num (_.0 . _.1))))
%      (num (_.0 . _.1)))
%     ((app (lam (tie-tag x.0 (var-tag x.0)))
%           (lam (tie-tag x.1 (var-tag _.0))))
%      (lam (tie-tag x.1 (var-tag _.0))))))

% Performance under Chez Scheme 7.3 with optimize-level 2 on Casper:

% (time (load "ak-lam-v.scm"))
%     11314 collections
%     35729 ms elapsed cpu time, including 16239 ms collecting
%     35737 ms elapsed real time, including 16300 ms collecting
%     12100440344 bytes allocated, including 12094863232 bytes reclaimed


% \chapauthor{J. P. Balthasar Mueller}
\chapter{Unsupervised Learning}

\begin{multicols}{2}[\subsubsection*{Contents of this chapter}]
   \printcontents{}{1}{\setcounter{tocdepth}{2}}
\end{multicols}

\section{Blind Source Separation}
Blind Source Separation or Blind Signal Separation (BSS) is the separation of mixed signals. An example is the cocktail party problem, where a microphone is recording the conversations of many people speaking at the same time, making the recording unintelligible. Successful BSS would be able to extract the voice of only a single person.

\section{Clustering}

\subsection{K-Means}
\subsection{Affinity Propagation}

\section{Independent Component Analysis (ICA)}
Independent Component Analysis (ICA) is a decomposition of data into components that have vanishing mutual information.

\section{Compressed Sensing}

\input{./chapters/sections/unsup_svd.tex}
\input{./chapters/sections/unsup_randomsvd.tex}
\input{./chapters/sections/unsup_pca.tex}
\input{./chapters/sections/unsup_nnmf.tex}
\input{./chapters/sections/unsup_truncation.tex}
\input{./chapters/sections/unsup_matrixcompletion.tex}
\input{./chapters/sections/unsup_autoencoders.tex}

\chapauthor{}


\documentclass[a4paper,11pt]{article}

\usepackage{physics}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsthm, mathtools}
%\usepackage{hyperref}
\usepackage{color}
\usepackage{jheppub}
\usepackage[T1]{fontenc} % if needed

% My Documents
\newcommand{\be}{\begin{equation}}
\newcommand{\ee}{\end{equation}}
\newcommand{\bes}{\begin{equation*}}
\newcommand{\ees}{\end{equation*}}
\newcommand{\bea}{\begin{flalign*}}
\newcommand{\eea}{\end{flalign*}}

%\linespread{1.0}
%\setlength{\parindent}{0em}
%\setlength{\parskip}{0.8em}

\title{\textbf{Vacuum Spacetimes}}
\author{Aditya Vijaykumar}
\affiliation{International Centre for Theoretical Sciences, Bengaluru, India.}
\emailAdd{aditya.vijaykumar@icts.res.in}
\abstract{This is a exploratory effort in understanding vacuum spacetimes from all aspects - analytical, numerical, geometrical - so that one can prepare oneself to understand the higher truths in the phenomenological and experimental results of such spacetimes.

	The main references are expected to be books on General Relativity by Prof. T Padmanabhan and Prof. Sean Carroll, the book on Numerical Relativity by Prof. Baumgarte and Prof. Shapiro, and Prof. Berti's Black Hole Perturbation Theory lecture notes. If the occasion demands, we might also refer to Prof. Maggiore's two tomes on Gravitational Waves. }
\begin{document}
\maketitle

\section{Review of General Relativity}
\textit{This will be a dynamical section, with changes based on the author's moods.}
\subsection{Local Inertial Frame}
Let us assume that a theory of gravity should be a tensor theory. One can justify this in many ways, but I particularly like how Padmanabhan presents this in his book as an \textit{inevitabilty}.

Right, so what do we have? We know that gravity should be described in geometrical terms, and when one speaks about some geometry, one should ascribe the notion of distance on that geometry. Hence, we define the interval as,
\begin{equation}
\dd s^2 = g_{ij} \dd x^i \dd x_j
\end{equation}
where  $ g_{ij}(x) $ is called the \textit{metric tensor}. In general, $ g_{ij} $ will depend on all spacetime components and will not be a constant, allowing us to calculate the curvature of our geometry. It is worthy to note that the presence of non-constant terms in $ g_{ij} $ does not mean that our space is necessarily curved. For example, consider two intervals written in 2-D spherical polar coordinates,
\begin{equation*}
\dd s_1^2 = \dd r^2 + r^2 \dd \theta^2 \qq{,} \dd s_2^2 = \dd r^2 + r^2 \sin^2 \theta \dd \theta^2
\end{equation*}
We know that $ \dd s_1 $ is just the flat space metric written in polar coordinates, while $ \dd s_2 $ is in fact the distance metric on the circumference of a circle. Both have non-constant matrix elements, but only one of them describes a truly curved space.

In principle, it should not be that hard to distinguish between curved and non-curved space. Consider the Minkowski metric $ \eta_{ij} $ and the transformation,
\begin{equation}\label{key}
g_{ij} = \eta_{ij} \pdv{x^i}{x'^a} \pdv{x^j}{x'^b} \implies \qq{four arbitrary functions of spacetime coordinates}
\end{equation}
But a general symmetric tensor in $ 4 $ dimensions has $ 10 $ independent components. So, a genuinely curved spacetime cannot be reduced to a flat metric globally by a coordinate transformation.

To see how curvature really can be distinguished from flat spacetimes, let us consider the general transformation law for a vector in the neighbourhood of a particular point,
\begin{equation}\label{key}
x'^i = A^i_j x^j + B^i_{jk} x^j x^k + C^i_{jkl} x^j x^k x^l + \ldots
\end{equation}
We want to try and make the neighbourhood of the point as \textit{flat} as possible by setting the metric $ g_{ij}= \eta_{ij} $ and all the metric derivatives to zero. Let's see if that is possible.
\begin{itemize}
	\item $ g_{ij} = \eta_{ij} \implies 10$ conditions, and we have $ 16 $ free parameters in $ A^i_j $. So this is certainly possible, with $ 6 $ parameters to spare. These $ 6 $ parameters correspond to the Lorentz rotations in $ 4 $-dimensions.
	\item $ \partial_a g^{ik} = 0 \implies 40 $ conditions. We have $ 40 $ free parameters in $ B^i_{jk} $, on account of it being symmetric in $ j $ and $ k $. So setting the first derivative to zero is also possible.
	\item $ \partial_a \partial_b g^{ik} = 0 \implies 100 $ conditions. In $ C^i_{jkl} $, we have $ N \times ^{4+2}C_3 = 80 $ free parameters. This means that, even after using all the freedom in $ C^i_{jkl} $, we cannot set the second derivative of the metric to zero.
\end{itemize}
So, the bottom line is that we can always choose coordinates around a point in such a way that the metric is Minkowski, and the first derivatives vanish. Such a coordinate system is called the local inertial frame at that point.

\subsection{Covariant Derivative and its Manipulations}
The covariant derivative is defined as :-
\begin{equation}\label{key}
\nabla_b v^a = \partial_b v^a + \Gamma^{a}_{bi} v^i
\end{equation}
Consider,
\begin{align*}
\nabla_b (v_a v^a) &= \partial_b (v_a v^a) \\
v_a \nabla_b v^a + v^a \nabla_b v_a&= 2 v_a \partial_b v^a \\
 v_a \partial_b v^a + v_a \Gamma^{a}_{bi} v^i + v^a \nabla_b v_a &=2  v_a \partial_b v^a \\
\nabla_b v_a &= \partial_b v_a - \Gamma^{i}_{ab} v_i
\end{align*}
Now, writing $ T^a_b = u^a v_b $, we get,
\begin{equation}\label{key}
\nabla_i T^a_b = \partial_i T^a_b + \Gamma^a_{ki} T^k_b - \Gamma^k_{bi} T^a_k
\end{equation}
Consider some identities for the connections $ \Gamma^i_{jk} $,
\begin{align}\label{key}
\Gamma^a_{ba} &= \dfrac{1}{2} g^{ad} \partial_b g^{ad }= \dfrac{1}{2g} \partial_b g = \partial_b\qty(\ln\sqrt{-g}) \\
g^{bc}  \Gamma^a_{bc} &= g^{bc} g^{ad} \qty(\partial_c g_{db} - \dfrac{1}{2} \partial_d g_{bc}) = - \dfrac{1}{\sqrt{-g}} \partial_b \qty(\sqrt{-g} g^{ab})
\end{align}
Let $ Q_{ab} $ be an antisymmetric tensor. Consider,
\begin{equation}\label{key}
\nabla_b Q^{ab} = \partial_b Q^{ab} + \Gamma^b_{db} Q^{ad}  = \dfrac{1}{\sqrt{-g}} \partial_b \qty(\sqrt{-g} Q^{ab})
\end{equation}

\section{Black Holes}
\subsection{Black Holes without Spin - Schwarzschild solution}

\end{document}


%!TEX root = ../thesis.tex
%*******************************************************************************
%*********************************** Forth Chapter *****************************
%*******************************************************************************

\chapter{Numerical Tests and Results}  %Title of the Forth Chapter

\ifpdf
    \graphicspath{{Chapter4/Figs/PDF/}{Chapter4/Figs/}}
\else
    \graphicspath{{Chapter4/Figs/}}
\fi

%********************************** %First Section  **************************************
\section{Multigrid solver tests} %Section - 4.1
\label{section4.1}
\subsection{Newtonian gravitational field}
For simplicity, we consider only the test of Poisson equation.
In Newtonian gravity, the gravitational potential $\Phi$ can be obtained by
\begin{align}\label{eq:poisson_eq}
    \nabla^2 = - 4\pi \rho,
\end{align}
where $\rho$ is the energy density of the matter source.
Here, we consider a spherically symmetric source as
\begin{align}
    \rho(r) &=
    \begin{cases}
        \rho_0 \left(1-r^2 \right), \quad & r<1,\\
        0, \quad & r\geq 1,
    \end{cases}
\end{align}
where $\rho_0$ is a constant chosen to be $1$.
The analytic solution is given by
\begin{align}
    \Phi(r) &=
    \begin{cases}
        \pi \rho_0 \left(\frac{r^4}{5} - \frac{2 r^2}{3} + 1 \right), \quad & r<1, \\
        \frac{8 \pi \rho_0}{15 r}, \quad & r\geq 1.
    \end{cases}
\end{align}
We solve the Poisson equation (\ref{eq:poisson_eq})
in 1D spherical, 2D cylindrical and 3D Cartesian coodinates
with 2nd order and 4th order central finite difference respectively.
The computational domain and the discretization are set to be
$0\leq r \leq 10$ and $[N]$ for spherical coordinate,
$0\leq (r,z) \leq 10$ and $[N,N]$ for cylindrical coordinate, and
$0\leq (x,y,z) \leq 10$ and $[N,N,N]$ for cylindrical coordinate respectively,
where $N$ is the number of grids.
The boundary conditions are set to be
\begin{align}
    \left. \frac{d \Phi}{d r} \right|_{r=0} &= 0 && \text{(Neumann boundary condition)}, \\
    \left. \frac{d r\Phi}{dr} \right|_{r=10} &= 0 && \text{(Robin boundary condition.)}
\end{align}
Note that although the boundition condition should be $\Phi \rightarrow 0$ for $r \rightarrow \infty$,
in practice we can only impose the Robin boundary condition for large $r$ due to the finite computational domain.

\begin{figure}
\centering
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{L1_norm_2.pdf}
  %\caption{A subfigure}
  \label{fig:mg_conv_4}
\end{subfigure}%
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{L1_norm_4.pdf}
  %\caption{A subfigure}
  \label{fig:mg_conv_4}
\end{subfigure}
\caption{L1-norm of $\Phi$ using 2nd order (left) and 4th order central finite difference scheme (right).}
\label{fig:mg_conv}
\end{figure}
Figure \ref{fig:mg_conv} shows the convergent rate of the L1-norm of $\Phi$
\begin{align}
    \left{||} \Phi \right{||}_1 &= \frac{1}{N}\sum^{N}_{i=1} \left| \Phi_i - \Phi^{exact}_i \right|,
\end{align}
where arround second-order accuracy and third-order convergence is achieved for 2nd order and 4th order central finite difference scheme in general.

%********************************** %Second Section  *************************************
\section{Teukolsky wave test}
\label{section4.2}
We consider the quadrupole $(l,m)=(2,0)$ mode of the vacuum solution.
For even-parity quadrupole mode, the metric takes form \cite{teukolsky1982linearized} in spherical coordinate $(r, \theta, \phi)$
\begin{align}
    \begin{split}
    ds^2 =& - dt^2 + \left(1 + A f_{rr}\right) dr^2 + \left(2 B f_{r\theta}\right) r dr d\theta
    + \left(2 B f_{r\phi} \right) r \sin\theta dr d\phi \\
    & \left(1 + C f^{(1)}_{\theta\theta} + A f^{(2)}_{\theta\theta} \right) r^2 d\theta^2
    + \left[2 \left(A-2C\right) f_{\theta\phi} \right] r^2 \sin\theta d\theta d\phi \\
    & + \left(1 + C f^{(1)}_{\phi\phi} + A f^{(2)}_{\phi\phi} \right) r^2 \sin^2\theta d\phi^2,
    \end{split}
\end{align}
where the coefficient $A,B,C$ are constructed from $F(x)$ for $x=t-r$ outgoing wave and $x=t+r$ ingoing wave
\begin{align}
    F(x) = F_1 (t-r) + F_2 (t+r),
\end{align}
and we define
\begin{align}
    F^{(n)} \coloneqq \left. \frac{d^n F_1(x)}{dx^n} \right|_{x=t-r} + (-1)^n \left. \frac{d^n F_2(x)}{dx^n} \right|_{x=t+r}.
\end{align}
Thus, the coefficient are given by
\begin{align}
    A &= 3 \left( \frac{F^{(2)}}{r^3} + \frac{3 F^{(1)}}{r^4} + \frac{3 F}{r^5} \right), \\
    B &= - \left( \frac{F^{(3)}}{r^2} + \frac{3F^{(2)}}{r^3} + \frac{3F^{(1)}}{r^4} + \frac{6F}{r^5} \right), \\
    C &= \frac{1}{4} \left( \frac{F^{(4)}}{r} + \frac{2F^{(3)}}{r^2} + \frac{9F^{(2)}}{r^3} + \frac{21F^{(1)}}{r^4} + \frac{21F}{r^5} \right).
\end{align}
For $m=0$ mode, the angular function $f_{ij}$ are given by
\begin{align}
    f_{rr} &= 2 - 3 \sin^2 \theta, & f_{r\theta} &= - 3 \sin\theta \cos\theta, & f_{r\phi} &= 0, \\
    f_{\theta\theta}^{(1)} &= 3 \sin^2\theta, & f_{\theta\theta}^{(2)} &= -1, & f_{\theta\phi} &= 0, \\
    f_{\phi\phi}^{(1)} &= - f_{\theta\theta}^{(1)}, & f_{\phi\phi}^{(2)} &= 3 \sin^2 \theta - 1.
\end{align}
In our test, we consider
\begin{align}
    F_1 (x) = - F_2 (x) = \frac{\mathcal{A}}{2} x e^{-\frac{x^2}{\lambda^2}},
\end{align}
and perform the simulation in 2D axisymmetric cylindrical coordinate $(r,z,\phi)$ with $z$ reflection symmetry.
The computational domain covers the region $0\leq \rho \leq 5$, $0\leq z \leq 5$
and the discretization of the domain is set to be $[256,256]$.
The inital profile is given by
\begin{align}
    \psi &= \alpha = 1, \\
    \beta^i &= 0, \\
    h^{rr} &= \mathcal{A}' \frac{\lambda^4 - \rho^2 z^2}{\lambda^8} e^{-\frac{x^2}{\lambda^2}}, \\
    h^{r z} &= \mathcal{A}' \frac{\rho z \left(\rho^2 - 2 \lambda^2 \right)}{\lambda^8} e^{-\frac{x^2}{\lambda^2}}, \\
    h^{r \phi} &= 0, \\
    h^{zz} &= - \mathcal{A}' \frac{\left(2 \lambda^4 - 4 \lambda^2 \rho^2 + \rho^4 \right)}{\lambda^8} e^{-\frac{x^2}{\lambda^2}}, \\
    h^{z\phi} &= 0, \\
    h^{\phi\phi} &= \mathcal{A}' \frac{\left(\lambda^4 - 4 \lambda^2 \rho^2 +\rho^2 z^2 + \rho^4 \right)}{\lambda^8} e^{-\frac{x^2}{\lambda^2}}, \\
    \hat{A}^{ij} &= 0,
\end{align}
where $\mathcal{A}' = \frac{\mathcal{A}}{12}$.
The analytic solution is written in the Appendix.
The simulation will be performed under two configurations:
\begin{enumerate}
    %\item Solve the linearized hyperbolic equations only
    %\begin{align}
    %    \partial_t h^{ij} &= 2 \hat{A}^{ij}, \\
    %    \partial_t \hat{A}^{ij} &= \frac{1}{2} f^{kl} \mathcal{D}_k \mathcal{D}_l h^{ij}.
    %\end{align}
    \item Solve the hyperbolic equations only (without elliptic divergence cleaning).
    \label{TW_config_1}
    \item Solve the hyperbolic equations togather with the elliptic sector in FCF except $\psi$, $\alpha$ and $\beta$ (with elliptic divergence cleaning).
    \label{TW_config_2}
\end{enumerate}
In the tests, we choose $\mathcal{A}'=10^{-4}$ and $\lambda=1$.
\begin{figure}[h!]
\centering
  \includegraphics[width=\linewidth]{herr.pdf}
\caption{L2-norm of $h^{ij}$ of the Teukolsky wave test by solving hyperbolic equations without elliptic sector (configuration \ref{TW_config_1}).}
\label{fig:TW_h_err}
\end{figure}
In figure \ref{fig:TW_h_err},
it shows the L2-norm of $h^{ij}$ which is given by
\begin{align}
    \delta h^{ij} &= \sqrt{\frac{1}{V}\sum \left| h^{ij} - h^{ij}_{exact} \right|^2}.
\end{align}
The simulation result agrees with the analytical solution very well.
\begin{figure}[h!]
\centering
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{Dh1.pdf}
  \caption{L2-norm of $\mathcal{D}_i h^{ir}$}
  \label{fig:Dh1}
\end{subfigure}%
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{Dh2.pdf}
  \caption{L2-norm of $\mathcal{D}_i h^{iz}$}
  \label{fig:Dh2}
\end{subfigure}
\caption{L2-norm of the divergence of $h^{ij}$.
The red line corresponds to the evolution of $h^{ij}$ without divergence cleaning
while the green line corresponds to the evolution of $h^{ij}$ with divergence cleaning.}
\label{fig:Dh}
\end{figure}
In figure \ref{fig:Dh}, it shows that the elliptic divergence cleaning method is effective on
reducing the divergence of $h^{ij}$.
Especially for $t \gtrsim 5$ when the wave is propagated out of the computational domain,
the divergence of $h^{ij}$ keep increasing if no treatment is applied,
while it is suppressed with the elliptic cleaning method.
\begin{figure}[h!]
\centering
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{result_hij_0000.pdf}
  %\caption{L2-norm of $\mathcal{D}_i h^{ir}$}
  \label{fig:hij9_0}
\end{subfigure}%
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{result_hij_0010.pdf}
  %\caption{L2-norm of $\mathcal{D}_i h^{ir}$}
  \label{fig:hij9_1}
\end{subfigure}%

\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{result_hij_0020.pdf}
  %\caption{L2-norm of $\mathcal{D}_i h^{ir}$}
  \label{fig:hij9_2}
\end{subfigure}%
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{result_hij_0030.pdf}
  %\caption{L2-norm of $\mathcal{D}_i h^{ir}$}
  \label{fig:hij9_3}
\end{subfigure}%

\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{result_hij_0040.pdf}
  %\caption{L2-norm of $\mathcal{D}_i h^{ir}$}
  \label{fig:hij9_4}
\end{subfigure}%
\begin{subfigure}{.5\textwidth}
  \centering
  \includegraphics[width=\linewidth]{result_hij_0050.pdf}
  %\caption{L2-norm of $\mathcal{D}_i h^{ir}$}
  \label{fig:hij9_5}
\end{subfigure}%

\caption{Evolution of $h^{\phi\phi}$ component between $t=0$ (upper left) and $t=5$ (lower right) with elliptic divergence cleaning.}
\label{fig:hij9}
\end{figure}
In figure \ref{fig:hij9}, we show the evolution of the $h^{\phi\phi}$ component with elliptic divergence cleaning.

%********************************** %Third Section  *************************************
\section{General relativistic hydrodynamics in dynamic spacetime} %Section - 4.3
\label{section4.3}
Here we study the evolution of a neutron star with dynamical background under FCF scheme and passive FCF scheme.
\subsection{Non-rotating neutron star}
\label{section4.3.1}
In this test, we consider a non-rotating neutron star known as "BU0" \cite{dimmelmeier2006non}
which is constructed with the polytropic index $\Gamma = 2$ and $K = 100$,
and central rest-mass density $\rho_c = 1.28 \times 10^{-3}$ (in $c=G=M_\odot=1$ unit).
This star model is generated by the open-source code \texttt{XNS}
\cite{bucciantini2011general,pili2014axisymmetric,pili2015general,pili2017general}.
We simulate the star in 2D cylindrical coordinate and impose $z$ reflection symmetry.
The computational domain covers $0 \leq r \leq 60$, $0\leq z \leq 60$ in code unit
with resolution $n_r \times n_z = 256 \times 256$.
The ideal-gas equation of state with $\Gamma = 2$ is used for the simulation.
We extract the plus polarization of gravitational wave signal $h_{+}$ at $(r_{ext}, z_{ext}) = (40, 0)$ which is given by
\begin{align}
    h_{+} \approx \frac{h^{\phi\phi} - h^{zz}}{2} \frac{R_{ext}}{R},
\end{align}
where $R=\sqrt{r^2+z^2}$ is the extraction radius.\\
\begin{figure}[h!]
\centering
  \includegraphics[width=\linewidth]{GW_combine_h_BU0.pdf}
\caption{$h_{+}$ extracted at $(r_{ext}, z_{ext}) = (40, 0)$ from the simulations of oscillating neutron star "BU0".
The vertical lines represent the known and well-tested eigenmode frequencies \cite{dimmelmeier2006non}}
\label{fig:GW_h_BU0}
\end{figure}
Figure \ref{fig:GW_h_BU0} shows the extracted gravitational wave signal $h_{+}$ from "BU0".
The extracted pulsation mode agrees with \cite{dimmelmeier2006non} for $F$ and ${}^2f$ mode.

\subsection{Rapidly rotating neutron star}
\label{section4.3.2}
In this test, we consider a rapidly-rotating neutron star known as "BU8" \cite{dimmelmeier2006non}.
The polytropic index, $K$ value and central rest-mass density are the same as "BU0",
except that it is uniformly rotating at an angular velocity of $\Omega = 2.633 \times 10^{-2}$.
The simulation setups are same as section \ref{section4.3.1},
and again we extracted $h_{+}$ at $(r_{ext}, z_{ext}) = (40, 0)$.\\
\begin{figure}[h!]
\centering
  \includegraphics[width=\linewidth]{GW_combine_h_BU8.pdf}
\caption{$h_{+}$ extracted at $(r_{ext}, z_{ext}) = (40, 0)$ from the simulations of oscillating neutron star "BU8".
The vertical lines represent the known and well-tested eigenmode frequencies \cite{dimmelmeier2006non}}
\label{fig:GW_h_BU8}
\end{figure}
Figure \ref{fig:GW_h_BU8} shows the extracted gravitational wave signal $h_{+}$ from "BU8".
The extracted pulsation mode agrees with \cite{dimmelmeier2006non} for ${}^2f$ mode in the case of using FCF scheme.
Note that the initial profile "BU8" is constructed under CFC approximation with zero $h^{ij}$,
which is not the case when full GR is considered.
This is why a spurious oscillation in $h$ occurs at the beginning of the simulation.
Nonetheless, $h$ quickly settles down and oscillates around the new equilibrium position.


\vssub
\subsubsection{~Second-order scheme (UNO)}
\opthead{UNO}{Met Office}{J.-G. Li}

\noindent
The UNO scheme for the directional $\theta$-space is identical to the regular
grid one assuming that the directional bins are regularly spaced. For the
\emph{k}-space, however, the UNO scheme uses its irregular version, which
uses local gradients instead of differences to estimate wave action value at
the mid-flux point for the cell face between spectral bin \emph{i}-1 and
\emph{i}, that is:
\begin{equation}
  N_{i-}^{*}=N_{c}+sign\left(N_{d}-N_{c}\right)\frac{\left(\Delta
      k_{c}-|\dot{k}_{i-}|\Delta
      t\right)}{2}\min\left(|\frac{N_{u}-N_{c}}{k_{u}-k_{c}}|,|\frac{N_{c}-N_{d}}{k_{c}-k_{d}}|\right)
  \:\:\:,
\label{eq:UNO2irregular}
\end{equation}

\noindent
where \emph{i}- is the wave number \emph{k} bin index; the subscripts
\emph{u}, \emph{c} and \emph{d} indicate the \emph{upstream, central} and
\emph{downstream} cells, respectively, relative to the given \emph{i}- face
velocity $\dot{k}_{i-}$; $k_{c}$ is the central bin wave number and $\Delta
k_{c}$ is the central bin widith. Details of the irregular grid UNO scheme are
given in \cite{art:Li08}.

Boundary conditions for the $\theta$-space is the natural periodic
condition. For the \emph{k}-space, two more zero spectral bins are added to
each end of the wave spectral domain as the UNO scheme is 2nd order in
accuracy.


\documentclass[11pt]{article}
\input{preamble/preamble}
\input{preamble/math-preamble.tex}

\begin{document}

\title{  {\LARGE STAT 578 Final Project: The Truncated SPRT} }

\author{
    Anamitra Chaudhuri \,
    Joshua Loyal
}

\date{\today}
\maketitle

\section{Introduction}
Consider $X_1, X_2, \dots \iidsim f_{\theta}$ where $f_{\theta}(x)$ belongs to the one-parameter exponential family, i.e. $f_{\theta}(x) = f_0(x)e^{x\theta - \psi(\theta)}$. Given $\theta_1 > \theta_0$, we want to design a test for
\begin{equation}\label{eq:hypo_test}
H_0: \theta \leq \theta_0 \text{ vs. } H_1: \theta \geq \theta_1
\end{equation}
such that the error probabilities are controlled at levels $\alpha, \beta$, i.e. $P_{\theta_0}(D = 1) \leq \alpha$ and $P_{\theta_1}(D = 0) \leq \beta$. In the fixed sample size (FSS) setting the Neyman-Pearson lemma states that the uniformly most powerful test for \ref{eq:hypo_test} that takes at most $M_{FSS}$ samples is the likelihood ratio test:
\begin{equation}
D_{FSS} = \ind{\sum_{i=1}^{M_{FSS}} Z_i \geq \tau}  = \ind{Z_{M_{FSS}}(\theta_1, \theta_0) \geq \tau}
\end{equation}
where $Z_i = \log(f_{\theta_1}(X_i) / f_{\theta_0}(X_i))$. When sample collection is expensive an appealing alternative is the class of sequential tests ($T, D$). Sequential tests allows us to take more than $M_{FSS}$ samples with the trade-off that the test's expected sample size (ESS) can be smaller than the equivalent FSS test. In his seminal work, \citet{wald1947} proposed the SPRT with stopping time
\begin{equation}
T_{SPRT} = \inf\set{n \geq 1 : Z_n(\theta_1, \theta_0) \notin (-\log(A), \log(B))}
\end{equation}
and decision rule
\begin{equation}
D_{SPRT} =
\begin{cases}
1, & Z_T(\theta_1, \theta_0) \geq \log(B) \\
0, & Z_T(\theta_1, \theta_0) \leq -\log(A).
\end{cases}
\end{equation}
Wald showed that this sequential procedure has a significantly lower expected sample size than the FSS test at $\theta \geq \theta_1$ and $\theta \leq \theta_0$ (in fact it is optimal at $\theta_0$ and $\theta_1$). A downside of the SPRT is that the ESS can be much larger than the FSS test in the indifference region $(\theta_0, \theta_1)$. Furthermore, this bad performance can be particularly egregious for the small error probabilities employed in practice.

This report explores the Truncated SPRT proposed by \citet{tantara1977} and \citet{tantara1982}, which alleviates the poor performance of the SPRT within the indifference region. In Section \ref{sec:trunc_sprt} we lay out the details and design of the Truncated SPRT. An interesting component of the Truncated SPRT is the introduction of mixing weights $c_1$ and $c_2$ that control how much the test performs like the FSS test or the SPRT. In Section \ref{sec:mixing_weights}, we numerically explore the effect of these parameters as well as demonstrate how these values are chosen in practice. Finally, in Section \ref{sec:comparisons}, we compare the Truncated SPRT with other popular sequential testing procedures on data simulated from a Gaussian and Bernoulli distribution.

\section{Method}\label{sec:method}

\subsection{The Truncated SPRT}\label{sec:trunc_sprt}

The Truncated SPRT runs a standard SPRT up until a pre-specified sample size $M$, after which it employs a fixed sample size (FSS) test to determine whether the hypothesis is rejected. The truncation of the test allows one to avoid an excessively large ESS in the worst case. This procedure is illustrated in Figure \ref{fig:trunc_sprt}.  Formally, the Truncated SPRT has a stopping time
\begin{equation}
T = T_{SPRT} \wedge M
\end{equation}
with the decision rule
\begin{equation}
D =
\begin{cases}
1, & T < M \text{ and } Z_T(\theta_1, \theta_0) \geq \log(B) \text{ or } T = M \text{ and } Z_T(\theta_1, \theta_0) \geq \tau \\
0, & T < M \text{ and } Z_T(\theta_1, \theta_0) \leq -\log(A) \text{ or } T = M \text{ and } Z_T(\theta_1, \theta_0) < \tau. \\
\end{cases}
\end{equation}
Note that there are four parameters that must be chosen: the SPRT thresholds $A$ and $B$, the truncation point $M$, and the threshold of the FSS test $\tau$.
\begin{figure}
\centering
\includegraphics[width=0.8\textwidth]{images/truncated_sprt}
\caption{Example of the Truncated SPRT procedure.}
\label{fig:trunc_sprt}
\end{figure}

One chooses $A, B, M, \tau$ to control the error probabilities at the desired levels $\alpha$ and $\beta$. The key result that makes the experimental design possible is the following bound on the error probabilities derived in \citet{tantara1977}:
\begin{equation}\label{eq:design_bound}
\begin{split}
P_{\theta_0}(D = 1) &\leq \alpha_{SPRT} + \alpha_{FSS} \\
P_{\theta_1}(D = 0) &\leq \beta_{SPRT} + \beta_{FSS}.
\end{split}
\end{equation}
Note that this follows from a union bound between the SPRT and FSS parts of the test.

With the bound in Equation \ref{eq:design_bound}, we can describe how to choose the test's parameters. We begin by choosing mixing weights $c_1, c_2 \in \left[0, 1\right]$ such that
\begin{equation}
\begin{split}
\alpha_{FSS} = c_1 \alpha, \quad &\alpha_{SPRT} = (1 - c_1) \alpha \\
\beta_{FSS} = c_2 \beta, \quad &\beta_{SPRT} = (1 - c_2) \beta.
\end{split}
\end{equation}
We then use Wald's recommendations to set the thresholds of the SPRT:
\begin{equation}
\begin{split}
A &= \frac{1 - \alpha_{SPRT}}{\beta_{SPRT}} \\
B &= \frac{1 - \beta_{SPRT}}{\alpha_{SPRT}}. \\
\end{split}
\end{equation}
Assuming that we can approximate the CDF of $Z_n(\theta_1, \theta_0)$ with a normal (true for the exponential family), we set the truncation level $M$ and FSS threshold $\tau$ to control the FSS error probabilities:
\begin{equation}
\begin{split}
M &= \frac{1}{(\mu_{\theta_1} - \mu_{\theta_0})^2} \left(\sigma_{\theta_1} z_{\beta_{FSS}} + \sigma_{\theta_0}z_{\alpha_{FSS}}\right)^2 \\
\tau &= \frac{\sqrt{M}}{\mu_{\theta_1} - \mu_{\theta_0}} \left(\mu_{\theta_1} \sigma_{\theta_0} z_{\alpha_{FSS}} + \mu_{\theta_0} \sigma_{\theta_1} z_{\beta_{FSS}}\right)
\end{split}
\end{equation}
where $\mu_{\theta} = E_{\theta}\left[Z_1(\theta_1, \theta_0)\right]$ and $\sigma^2_{\theta} = \text{Var}_{\theta}\left[Z_1(\theta_1, \theta_0)\right]$. Because $\alpha_{FSS} + \alpha_{SPRT} = \alpha$ and $\beta_{FSS} + \beta_{SPRT} = \beta$, these settings achieve the desired error control.

\subsection{Trade-off}

There is a trade-off between the performance at the truncation point $M$ and the expected sample size of the truncated test. A lower truncation point requires $\alpha_{FSS}$ and $\beta_{FSS}$ to be higher and therefore $\alpha_{SPRT}$ and $\beta_{SPRT}$ to be lower. This means SPRT takes longer to terminate and the performance at $\theta_1$ (and $\theta_0$) becomes worse. But on the other hand, performance at the indifference region gets better due to a lower truncation point. This is illustrated in Figure \ref{fig:trade-off}. This trade-off can also be observed in the power function of the tests. Allowing more error to the FSS test and less error to the SPRT makes the truncated test less conservative than SPRT but for the same reason ESS gets higher at $\theta_0$ and $\theta_1$. Therefore in order to achieve the criteria for a desired test it is important to carefully tune $c_1$ and $c_2$, which is discussed in the next section.

\begin{figure}
\centering
\begin{subfigure}{0.49\textwidth}
    \includegraphics[width=\textwidth]{images/trade-off.pdf}
\end{subfigure}
\hfill
\begin{subfigure}{0.49\textwidth}
    \includegraphics[width=\textwidth]{images/PowerFunction.pdf}
\end{subfigure}
\caption{The trade-off between truncation point $M$ and the ESS of the test.}
\label{fig:trade-off}
\end{figure}

\subsection{Choosing the Mixing Weights}\label{sec:mixing_weights}

The design of the test is complete once one chooses the mixing weights $c_1$ and $c_2$. If the truncation level is unconstrained in the experimental design, it is recommended to monitor the following quantities:
\begin{equation}\label{eq:c_optim}
\frac{E_{\theta_{*}}[T\,]}{M_{FSS}}, \, \frac{M}{M_{FSS}}  \, \text{ and } \, \frac{E_{\theta_0}[T\,]}{E_{\theta_0}[T_{SPRT}]}, \frac{E_{\theta_1}[T\,]}{E_{\theta_1}[T_{SPRT}]}.
\end{equation}
The first two quantities should be smaller than one, which indicates that the sequential test takes less samples than the FSS test on average in the worst case. The second two quantities should be close to one in order to match the optimal performance of the SPRT at $\theta_0$ and $\theta_1$. Alternatively, one may have a maximum allowable truncation point $M$ dictated by the experiment. For example, in quality control experiments this is the total number of items in the lot. In this case...

We consider two different experimental designs. The first is one in which the truncation level is allowed to be unconstrained and later is in which the truncation level is set to a maximum allowable value specified by some industrial design.

\section{Numerical Results}

For our simulations we will consider the following two scenarios:

\textit{Scenario 1 (Gaussian):} $X_1, X_2, \dots \iidsim N(\theta, 1)$ and we want to test $H_0 : \theta \leq -\delta$ vs. $H_1 : \theta \geq \delta$. Note that the FSS thresholds are
\begin{equation}
\begin{split}
M &= \frac{(z_{\alpha_{FSS}} + z_{\beta_{FSS}})^2}{4 \delta^2} \\
\tau &= \sqrt{M} \delta (z_{\alpha_{FSS}} - z_{\beta_{FSS}}).
\end{split}
\end{equation}

\textit{Scenario 2 (Bernoulli):} $X_1, X_2, \dots, \iidsim \text{Bernoulli}(p)$ and we want to test $H_0: p = p_0$ vs. $H_1: p = p_1$.

\subsection{The Effect of the Mixing Weights on the Test}

\begin{figure}
\centering
\includegraphics[width=0.8\textwidth]{images/c1_c2_ratios}
\caption{Effect of $c_1$ and $c_2$ on the quantities in Equation \ref{eq:c_optim} for the Gaussian example.}
\end{figure}

\subsection{Comparison with Other Sequential Tests}\label{sec:comparisons}

For large error probabilities (0.1 or $10^{-2}$, the Truncated SPRT generally performs worse or equivalent to Lorden's 2-SPRT and the GLR test. For small error probabilities, the Truncated SPRT has uniformly lower ESS between 0.5 and $-0.5$, but has much heavy tails for large $\theta$. All tests besides the SPRT have a smaller sample size on average than the equivalent FSS test.

\begin{figure}
\centering
\begin{subfigure}{0.49\textwidth}
    \includegraphics[width=\textwidth]{images/ess_alpha1e1}
\end{subfigure}
\hfill
\begin{subfigure}{0.49\textwidth}
    \includegraphics[width=\textwidth]{images/ess_alpha1e2}
\end{subfigure}
\begin{subfigure}{0.49\textwidth}
    \includegraphics[width=\textwidth]{images/ess_alpha1e3}
\end{subfigure}
\hfill
\begin{subfigure}{0.49\textwidth}
    \includegraphics[width=\textwidth]{images/ess_alpha1e4}
\end{subfigure}

\caption{Comparison for the SPRT, Truncated SPRT, Lorden's 2-SPRT, and the GLR test when $X_i \iidsim N(\theta, 1)$, $\theta_0 = -\theta_1 = 0.4$, and thresholds are choosen such that the error probabilities are $\alpha = \beta = 0.1, 10^{-2}, 10^{-3}, 10^{-4}$. The mixing proportion was set to $c_1 = c_2 = 0.5$ for the Truncated SPRT. }
\end{figure}

\section{Conclusion}

\bibliographystyle{asa}
\bibliography{reference}

\end{document}