\title{Continuum Stewart-Gough Dynamics}
\author{John Till}
\date{}

\documentclass[12pt]{article}

\usepackage[a4paper, margin=0.75in]{geometry}
\usepackage[colorlinks=true,urlcolor=blue]{hyperref}
\usepackage{amsmath,amssymb}
\usepackage{graphicx}

\usepackage{xcolor}
\definecolor{OffWhite}{rgb}{0.93,0.93,0.93}
\definecolor{QtCommentColor}{rgb}{0,0.5,0}
\definecolor{QtKeywordColor}{rgb}{0.5,0.5,0}
\definecolor{QtPurpleColor}{rgb}{0.5,0,0.5}
\definecolor{QtGlobal}{rgb}{0.808,0.361,0}
\definecolor{QtFunctionColor}{rgb}{0,0.404,0.486}
\definecolor{BlenderPythonString}{rgb}{0.392,0,0}
\definecolor{BlenderPythonKeyword}{rgb}{0.502,0,0.314}
\definecolor{BlenderPythonGold}{rgb}{0.373,0.373,0}
\definecolor{BlenderPythonLiteral}{rgb}{0,0,0.784}
\definecolor{BlenderPythonBackground}{rgb}{0.6,0.6,0.6}

\usepackage[T1]{fontenc} %for upquotes in listings
\usepackage{textcomp} %for upquotes in listings
\usepackage{listings}
\lstset{
		language=C++,
		escapeinside={!-}{-!},
		upquote=true,
		%
		otherkeywords={Vector3d, DiagonalMatrix, VectorXd, Matrix3d, Map, MatrixXd, TimeManagerBdfAlpha,
		              Vector6d, std, fstream,
									UnitX, pow, inverse, transposeMultiply, segment, data, UnitZ, cross, hat_postmultiply,
									Zero, Identity, UnitY, cosseratRodOde, cosseratRodPDE, ode4, col, row, main, plot,
									solveLevenbergMarquardt, Ones, linear_rotation_error, transpose, generateRotation,
									TimeBdfAlpha_SpaceEuler, euler, objFunc, block, clock, advanceTime, endl,
									playAnimation, getC0, obj, initialize_StewartGough_pattern, resize, close},
    morekeywords=[2]{Vector3d, DiagonalMatrix, VectorXd, Matrix3d, Map, MatrixXd, TimeManagerBdfAlpha,
		                 Vector6d, std, fstream},
		morekeywords=[3]{UnitX, UnitZ, pow, inverse, transposeMultiply, segment, data, cross, hat_postmultiply,
		                 Zero, Identity, UnitY, cosseratRodOde, cosseratRodPDE, ode4, col, row, main, plot,
										solveLevenbergMarquardt, Ones, linear_rotation_error, transpose, generateRotation,
										TimeBdfAlpha_SpaceEuler, euler, objFunc, block, clock, advanceTime, endl,
										playAnimation, getC0, obj, initialize_StewartGough_pattern, resize, close},
    %
		frame = single,
		rulecolor=\color{black},
    tabsize=4, % tab space width
    showstringspaces=false, % don't mark spaces in strings
		%
		basicstyle=\footnotesize,%\color{QtIdentifier},
		backgroundcolor=\color{OffWhite},
    commentstyle=\color{QtCommentColor}, % comment color
    keywordstyle=\color{QtKeywordColor}, % keyword color
		keywordstyle=[2]{\color{QtPurpleColor}},
		keywordstyle=[3]{\color{QtFunctionColor}},
    stringstyle=\color{QtCommentColor} % string color
}

\begin{document}

\makeatletter
\renewcommand{\@maketitle}{
\newpage
\null
\vskip 2em
\begin{center}
{\LARGE \@title \par}
\end{center}
\par
} \makeatother

\maketitle

Simulating the dynamics of a continuum Stewart-Gough (CSG) robot is an extension of the previous examples to solve elastic rod dynamics and to solve the static CSG kinematics. The theoretical development is described in the paper \href{https://journals.sagepub.com/doi/10.1177/0278364919842269}{``Real-Time Dynamics of Soft and Continuum Robots based on Cosserat-Rod Models''}.

\section{Simulation}

In the first part we implement the CSG dynamics in a C++ script. We start by defining the independent parameters:
\begin{lstlisting}
//Independent Parameters
const double E = 207e9;
const double rad = 0.001;
const double rho = 8000;
const Vector3d g = -9.81*Vector3d::UnitZ();
const double scrib_R = 0.087; //radius of the hole pattern
const double alpha1 = 100*pi/180; //major angle of the hole pattern
const double plate_mass = 0.0921;
const DiagonalMatrix<double, 3> plate_mass_moment(1.91e-4, 1.91e-4, 3.82e-4);
const int N = 200; //Number of spatial points
const double dt = 1.0/120.0; //Time step
const double alpha = -0.2; //Parameter for implicit time discretization
const Vector3d p_start(0, 0, 0.5);
const Vector3d p_final(-0.2, 0, 0.5);
const int num_actuated = 300;
const int num_swaying = 300;
\end{lstlisting}
Many of these variables are design parameters for a continuum Stewart-Gough robot. This includes the plate mass and plate inertia since we will consider the rigid-body dynamics of the end effector. After the CSG parameters, we have the BDF-$\alpha$ parameters ``dt'' and ``alpha''. The last four parameters are specific to this simulation. We will solve the CSG inverse kinematics to generate a trajectory for which the quasi-static model begins at ``p\_start'' and moves to ``p\_final''. However, the target points have been chosen so that this trajectory causes elastic instability, and following the trajectory using the CSG forward dynamics model will cause the robot to snap. The number of time steps spent following the input trajectory is ``num\_actuated'', and ``num\_swaying'' is the number of time steps to continue simulating after the actuator values no longer change.

\newpage \noindent
With the independent parameters taken care of, we have various dependent parameters which may be calculated:
\begin{lstlisting}
//Dependent parameter calculations
const double c0 = TimeManagerBdfAlpha::getC0(dt,alpha);
const double G = E/(2*(1+0.3));
const double A = pi*pow(rad,2);
const double I = pi*pow(rad,4)/4;
static Vector3d pE = p_start;
static Matrix3d RE = Matrix3d::Identity();
const double alpha2 = 120*pi/180 - alpha1; //minor angle of the hole pattern
const DiagonalMatrix<double, 3> Kse_inv =
    DiagonalMatrix<double, 3>(G*A,G*A,E*A).inverse();
const DiagonalMatrix<double, 3> Kbt_inv =
    DiagonalMatrix<double, 3>(E*I,E*I,G*2*I).inverse();
const DiagonalMatrix<double, 3> rhoJ = rho*DiagonalMatrix<double, 3>(I, I, 2*I);
const double rhoA = rho*A;
const Vector3d rhoAg = rho*A*g;
static Vector6d L;
\end{lstlisting}
The forward problem (kinematic or dynamic) is to solve for ``pE'' and ``RE'' given the leg lengths ``L'', and the inverse problem is to solve for ``L'' given ``pE'' and ``RE''. These variables are declared static since they will change throughout the simulation, while the other parameters are declared constant.

The script has a function to setup the CSG hole pattern as in the static CSG BVP and functions for the ODE and PDE as in the dynamic rod problem. These are fundamentally unchanged, so we won't consider them in detail here. Compared to the dynamic rod problem, the CSG requires several more state and dynamic variables:
\begin{lstlisting}
static MatrixXd Y[6], Z[6], Z_h[6];
static MatrixXd X(24,1), X_h(24,1);
const Map<Vector3d> pE_h(&X_h(0,0));
const Map<Vector3d> vE_h(&X_h(3,0));
const Map<Matrix3d> RE_h(&X_h(6,0));
const Map<Vector3d> wE_h(&X_h(15,0));
const Map<Vector6d> L_h(&X_h(18,0));
\end{lstlisting}
We have states ``Y'' and dynamic variables ``Z'' for each rod. We also have several dynamic variables not associated with the rods. We lump these into a variable ``X'', and then create maps to reference the meaningful components of X.

Since there are strong similarities in the inverse kinematics, forward kinematics, and forward dynamics objective functions, we combine them all into a single templated function. We begin this function by addressing the dissimilarities in the guess:
\begin{lstlisting}
template<bool is_forward, bool is_dynamic>
VectorXd obj(VectorXd guess){
    VectorXd residual(42);

    if(is_forward){
        pE = guess.segment<3>(36);
        RE = generateRotation(guess.segment<3>(39));
    }else{
        L = guess.segment<6>(36);
    }
\end{lstlisting}
For this simulation we consider a robot with the rods fully attached so that they support torsion and cannot spin, which results in a 42x42 system of nonlinear equations. The last six elements of the guess are the leg lengths for the inverse problem, and in the forward problem the last six elements are used to generate the end-effector pose. The ``generateRotation'' function uses Rodrigues' rotation formula to generate a rotation matrix from a 3x1 vector.

In the next step we solve for the dynamic movement of the end effector and create variables to sum the error in the plate's rigid body dynamics:
\begin{lstlisting}
    Matrix3d Jg = RE*plate_mass_moment*RE.transpose();
    Vector3d vE, wE, EF, EM;
    Vector6d L_t;
    if(is_dynamic){
        vE = c0*pE + pE_h;
        Vector3d aE = c0*vE + vE_h;
        wE = inv_hat((c0*RE+RE_h)*RE.transpose());
        Vector3d wE_t = c0*wE + wE_h;
        L_t = c0*L + L_h;

        EF = plate_mass*(g - aE);
        EM = -Jg*wE_t - wE.cross(Jg*wE);
    }else{
        vE = wE = Vector3d::Zero();
        L_t = Vector6d::Zero();

        EF = plate_mass*g;
        EM = Vector3d::Zero();
    }
    X << pE, vE, Map<VectorXd>(RE.data(), 9), wE, L;
\end{lstlisting}
If we are considering dynamic behavior, the force and moment equilibrium errors ``EF'' and ``EM'' will include inertial effects from the plate mass. In both cases we set ``X'' so that we can track its history.

Finally we can integrate the rods and finish calculating the residual:
\begin{lstlisting}
    for(int i = 0; i < 6; i++){
        Vector3d n0 = guess.segment<3>(6*i);
        Vector3d m0 = guess.segment<3>(6*i+3);
        double wz0 = 0; //Assume negligible torsional velocity through baseplate
        VectorXd y0(24);
        y0 << p0[i], 1, 0, 0, 0, 1, 0, 0, 0, 1, n0, m0, 0, 0, L_t(i), 0, 0, wz0;
        Y[i].col(0) = y0;

        //Numerically integrate the Cosserat rod equations
        if(is_dynamic) TimeBdfAlpha_SpaceEuler<cosseratRodPDE,24,12,N>
                                              (Y[i],Z[i],y0,L(i),Z_h[i]);
        else euler<cosseratRodOde,24,12,N>(Y[i],Z[i],y0,L(i));

        Vector3d pL_shot = Y[i].block<3,1>(0, N-1);
        Matrix3d RL_shot = Map<Matrix3d>(Y[i].block<9,1>(3, N-1).data());
        Vector3d nL = Y[i].block<3,1>(12, N-1);
        Vector3d mL = Y[i].block<3,1>(15, N-1);

        residual.segment<3>(6*i) = pL_shot - (pE + RE*r[i]);
        residual.segment<3>(6*i+3) = linear_rotation_error(RL_shot,RE);

        EF -= nL;
        EM -= (mL + (RE*r[i]).cross(nL));
    }
    residual.segment<3>(36) = EF;
    residual.segment<3>(39) = EM;

    return residual;
}
\end{lstlisting}
The only variability in the approach is whether we integrate using a static or dynamic method. In all cases, we integrate a rod, calculate the geometric errors, and continue summing the equilibrium errors until we have the full residual vector.

With the objective function finished, we have all the subfunctions we need and we can move on to the main method. Of course we begin by setting up the hole pattern:
\begin{lstlisting}
int main(int, char**){
    initialize_StewartGough_pattern();
\end{lstlisting}
Calling the forward dynamic objective function with ``obj<true,true>'' is not very intuitive, but we can fix this by defining several Booleans:
\begin{lstlisting}
const bool Kinematics = false;
const bool Dynamics = true;
const bool Forward = true;
const bool Inverse = false;
\end{lstlisting}
This allows us to refer to the forward dynamics function as ``obj<Forward,Dynamics>'' and the inverse kinematics as ``obj<Inverse,Kinematics>''.

When we declared the arrays for the rod variables, it was not convenient to initialize their sizes in the declaration. We set the sizes of these variables here:
\begin{lstlisting}
//Setup the grid
for(int i = 0; i < 6; i++){
    Y[i].resize(24,N);
    Z[i].resize(12,N);
    Z_h[i].resize(12,N);
}
\end{lstlisting}
Now we have done the setup to actually start simulating. The first half of the simulation is the inverse kinematics problem of solving the motor inputs to realize the trajectory:
\begin{lstlisting}
//Solve the "home" configuration
VectorXd guess(42);
guess << VectorXd::Zero(36), pE(2)*VectorXd::Ones(6);
guess = solveLevenbergMarquardt<obj<Inverse,Kinematics> >(guess);

//Declare actuation profile and set first entry
Vector6d leg_lengths[num_actuated+1];
leg_lengths[0] = guess.segment<6>(36);

//Inverse kinematics to find the input trajectory
for(int i = 1; i <= num_actuated; i++){
    std::cout << "IK: " << i << std::endl;

    pE = p_start + (p_final - p_start)*i/static_cast<double>(num_actuated);
    guess = solveLevenbergMarquardt<obj<Inverse,Kinematics> >(guess);
    leg_lengths[i] = guess.segment<6>(36);
}
\end{lstlisting}
We linearly interpolate between the start and final points. Solved values are stored in our ``leg\_lengths'' array.

\newpage \noindent
Now we move on to the second half of the simulation: the forward dynamics. We need to use a few lines to declare variables and set up the ICs:
\begin{lstlisting}
//Reset the robot state and solve ICs
pE = p_start;
L = leg_lengths[0];
guess << VectorXd::Zero(36), p_start, Vector3d::Zero();
guess = solveLevenbergMarquardt<obj<Forward,Kinematics> >(guess);

//Prepare for the dynamic problem
int total_frames = 1 + num_actuated + num_swaying;
MatrixXd centerlines(6*3*total_frames,N), g_frames(3*total_frames,4);
VectorXd pEx(total_frames), pEz(total_frames);
TimeManagerBdfAlpha* time_scheme[7];
for(int i = 0; i < 6; i++)
    time_scheme[i] = new TimeManagerBdfAlpha(Z[i], Z_h[i], dt, alpha);
time_scheme[6] = new TimeManagerBdfAlpha(X, X_h, dt, alpha);
\end{lstlisting}
The guess is reset, and the last six elements will represent the pose now. We solve the forward kinematics problem to reset the robot to the starting position, and we get ready to calculate time derivatives using our time manager abstraction. We create four variables, ``centerlines'', ``g\_frames'', ``pEx'', and ``pEz'', which we will use to store the simulation results.

Now we have the dynamic simulation loop:
\begin{lstlisting}
//Simulate the dynamic behavior
for(int i = 0; i < total_frames; i++){
    std::cout << "Dynamic: " << i << std::endl;

    if(i <= num_actuated) L = leg_lengths[i];
    guess = solveLevenbergMarquardt<obj<Forward,Dynamics> >(guess);
    for(int j = 0; j < 7; j++) time_scheme[j]->advanceTime();

    pEx(i) = pE.!-\textcolor{QtFunctionColor}{x}-!();
    pEz(i) = pE.!-\textcolor{QtFunctionColor}{z}-!();
    g_frames.block<3,3>(3*i,0) = RE;
    g_frames.block<3,1>(3*i,3) = pE;
    for(int j = 0; j < 6; j++)
        centerlines.block<3,N>(18*i+3*j,0) = Y[j].block<3,N>(0,0);
}
\end{lstlisting}
For each time step we set the leg lengths based on the input motion profile we solved earlier, then we solve the forward dynamics problem and calculate dynamic terms for the next step. The last six lines of the loop are used to store simulation results. In the final lines after the dynamic loop, we save the data to a couple files and plot the dynamic tip trajectory:
\begin{lstlisting}
//Save results for Blender visualization
std::fstream file("centerlines.dat", std::fstream::out);
file << centerlines;
file.close();

file = std::fstream("ee_transformations.dat", std::fstream::out);
file << g_frames;
file.close();

//Show the end-effector trajectory
#ifdef QT_CORE_LIB
plot(pEx, pEz, "CSG Dynamic Trajectory", "x (m)", "z (m)");
#endif
\end{lstlisting}
Running the program causes a graph of the end-effector trajectory to popup:
\begin{figure}[h]
	\centering
		\includegraphics[width=0.9\textwidth]{fig/SolutionPlot.jpg}
	\label{fig:SolutionPlot}
\end{figure}

\noindent The largest saved file contains all six rod centerlines at every time step, and the smaller file contains the end-effector position and rotation at each time step. We could visualize this data using Qt, but to keep this simulation script short and to make a shiny video in Blender, we'll handle visualization in a second part.

The program takes some time to run. With enough effort we could make the simulation run in soft real-time by writing a user-supplied Jacobian function, piddling with the hot-loop in the integration routine, and multithreading. However, the current simulation script is almost 300 lines, and we've already seen how to make all those changes, so we won't incorporate those changes here.

\section{Visualization}

One of the nice features of Blender is that in addition to using the user interface, you can also perform modeling tasks using Python scripts. This is perfect for generating 3d models from simulation results.

The simulation output files ``centerlines.dat'' and ``ee\_transformations.dat'' should be moved to the ``Blender'' subfolder. This task isn't automated since the directory in which the simulation output files are generated will depend on how the C++ code is run. If run with Qt, the output files should be in the build folder. Once these files are moved, the Blender script is ready to run.

Upon opening the ``CSG\_Animation.blend'' file, the Python scripting pane will be in the upper left. There is a ``Run Script'' button on the bottom right of the pane as shown below:
\begin{figure}[h]
	\centering
		\includegraphics[width=0.95\textwidth]{fig/ScriptingPane.jpg}
	\label{fig:Pane}
\end{figure}

\noindent I recommend using ``Window $\rightarrow$ Toggle System Console'' prior to running a script because this will show the output and allow you to stop a running script by hitting ``Ctrl+C'' with the console selected.

\lstset{
		language=Python,
		escapeinside={!-}{-!},
		%
		deletekeywords={def, type, object, range, len, int},
		otherkeywords={def, True, False, type, object, range, len},
		morekeywords=[2]{def},
		morekeywords=[3]{True, False, type, object, range, len},
		%
		frame = single,
		rulecolor=\color{black},
    tabsize=4, % tab space width
    showstringspaces=false, % don't mark spaces in strings
		%
		basicstyle=\footnotesize,%\color{QtIdentifier},
		%backgroundcolor=\color{BlenderPythonBackground},
    %commentstyle=\color{QtCommentColor}, % comment color
    keywordstyle=\color{BlenderPythonKeyword}, % keyword color
		keywordstyle=[2]{\color{BlenderPythonGold}},
		keywordstyle=[3]{\color{BlenderPythonLiteral}},
    stringstyle=\color{BlenderPythonString}, % string color
		literate={0}{{\textcolor{BlenderPythonLiteral}{0}}}{1}%
             {1}{{\textcolor{BlenderPythonLiteral}{1}}}{1}%
             {2}{{\textcolor{BlenderPythonLiteral}{2}}}{1}%
             {3}{{\textcolor{BlenderPythonLiteral}{3}}}{1}%
             {4}{{\textcolor{BlenderPythonLiteral}{4}}}{1}%
             {5}{{\textcolor{BlenderPythonLiteral}{5}}}{1}%
             {6}{{\textcolor{BlenderPythonLiteral}{6}}}{1}%
             {7}{{\textcolor{BlenderPythonLiteral}{7}}}{1}%
             {8}{{\textcolor{BlenderPythonLiteral}{8}}}{1}%
             {9}{{\textcolor{BlenderPythonLiteral}{9}}}{1}%
             {.0}{{\textcolor{BlenderPythonLiteral}{.0}}}{2}% Following is to ensure that only periods
             {.1}{{\textcolor{BlenderPythonLiteral}{.1}}}{2}% followed by a digit are changed.
             {.2}{{\textcolor{BlenderPythonLiteral}{.2}}}{2}%
             {.3}{{\textcolor{BlenderPythonLiteral}{.3}}}{2}%
             {.4}{{\textcolor{BlenderPythonLiteral}{.4}}}{2}%
             {.5}{{\textcolor{BlenderPythonLiteral}{.5}}}{2}%
             {.6}{{\textcolor{BlenderPythonLiteral}{.6}}}{2}%
             {.7}{{\textcolor{BlenderPythonLiteral}{.7}}}{2}%
             {.8}{{\textcolor{BlenderPythonLiteral}{.8}}}{2}%
             {.9}{{\textcolor{BlenderPythonLiteral}{.9}}}{2}%
						 {type}{type}{4}%
						 {object}{object}{6}%
						 {range}{range}{5}%
						 {len}{len}{3}%
						 {int}{int}{3}%
						 {format}{format}{6}%
}

This script has two parts. First, it creates a JPEG image of the robot for each time step of the simulation. For each frame it loops through the rod centerlines and uses a helper function ``drawRod'' to create the rod shape. It also reads the end-effector pose and draws a transparent plate using the ``drawEndEffector'' function. Then the script renders the scene and saves a JPEG before deleting everything to start on the next frame.
\begin{figure}[h]
	\centering
		\includegraphics[width=0.3\textwidth]{fig/Render.jpg}
	\label{fig:Render}
\end{figure}

In the second part, the script stitches the frames together into an AVI movie of the simulation using Blender's ``sequencer''. The movie is generated in the subfolder ``Blender/output''. The resolution of 480x240 is rather low, but this greatly decreases the file sizes and time to run the script compared to a 1080p movie.

A detailed explanation of the script is a bit tangential, and besides that I am not quite confident enough in my Blender or Python skills to discuss the code at length. But the basic elements of what we need to visualize continuum robots are present, and using this for other rod elasticity problems would require minor adaptations. The script is given here:

\begin{lstlisting}
import bpy
from bpy import context
import numpy as np
import mathutils
from mathutils import Matrix, Vector
import os
import shutil

def drawEndEffector(p,R):
    rotm = Matrix(R)
    (x_base, y_base, z_base) = Vector(p) + rotm * Vector((0, 0, -0.003/2))
    (x_top, y_top, z_top) = Vector(p) + rotm * Vector((0, 0, 0.003/2))

    #Curve settings
    curveData = bpy.data.curves.new('myCurve', type='CURVE')
    curveData.dimensions = '!-\textcolor{BlenderPythonString}{3D}-!'
    curveData.resolution_u = 2
    polyline = curveData.splines.new('POLY')
    polyline.points.add(1)
    polyline.points[0].co = (x_base, y_base, z_base, 1)
    polyline.points[1].co = (x_top, y_top, z_top, 1)

    cross_section = bpy.ops.curve.primitive_bezier_circle_add(radius=0.091)
    ob = bpy.context.object
    ob.name = 'cross_section_ee'

    curveOB = bpy.data.objects.new('myCurve', curveData)
    scn = bpy.context.scene
    scn.objects.link(curveOB)
    scn.objects.active = curveOB
    curveOB.select = True
    curveOB.data.bevel_object = bpy.data.objects['cross_section_ee']
    curveOB.data.use_fill_caps = True

    mat = bpy.data.materials.new("RGBA")
    mat.use_transparency = True;
    mat.diffuse_color = (117/255.0, 214/255.0, 1)
    mat.alpha = 0.5;
    curveOB.active_material = mat

def drawRod(p):
    (rows,cols) = p.shape

    #Curve settings
    curveData = bpy.data.curves.new('tCurve', type='CURVE')
    curveData.dimensions = '!-\textcolor{BlenderPythonString}{3D}-!'
    curveData.resolution_u = 2
    polyline = curveData.splines.new('POLY')
    polyline.points.add(cols-1)

    cross_section = bpy.ops.curve.primitive_bezier_circle_add(radius=0.001)
    ob = bpy.context.object
    ob.name = 'tcross_section'

    #Loop through spatial steps
    for i in range(0,cols):
    	x,y,z = p[0,i], p[1,i], p[2,i]
    	polyline.points[i].co = (x, y, z, 1)

    curveOB = bpy.data.objects.new('myCurve', curveData)
    scn = bpy.context.scene
    scn.objects.link(curveOB)
    scn.objects.active = curveOB
    curveOB.select = True
    curveOB.data.bevel_object = bpy.data.objects['tcross_section']
    curveOB.data.use_fill_caps = True

### MAIN ###
script_dir = os.getcwd() #<-- absolute path of dir the script is in
bpy.context.scene.world.horizon_color = (1,1,1) #white background
bpy.data.scenes["Scene"].render.image_settings.file_format = 'JPEG'
bpy.data.scenes["Scene"].render.use_sequencer = False
scene = bpy.data.scenes["Scene"]
scene.render.resolution_x = 480*2
scene.render.resolution_y = 240*2

#Load data
centerline_data = np.loadtxt( os.path.join(script_dir, 'centerlines.dat'))
ee_data = np.loadtxt( os.path.join(script_dir, 'ee_transformations.dat'))
(rows,cols) = centerline_data.shape
total_frames = int(rows/18);

#Loop through time steps to create frames
for i in range(0,total_frames):
    ee_pos = ee_data[3*i:3*i+3, 3];
    ee_rot = ee_data[3*i:3*i+3, 0:3];
    drawEndEffector(ee_pos, ee_rot);
    for j in range(0,6):
        p = centerline_data[ 18*i+3*j:18*i+3*j+3, 0:cols ];
        drawRod(p)

    rel_path = 'temp/frame_' + format(i, '!-\textcolor{BlenderPythonString}{05d}-!') + '.jpg'
    abs_file_path = os.path.join(script_dir, rel_path)
    bpy.data.scenes["Scene"].render.filepath = abs_file_path
    bpy.ops.render.render( animation=False, write_still=True )

    for item in bpy.data.objects:
        if item.type == 'MESH' or item.type=='CURVE':
            item.select = True
            bpy.ops.object.delete() #clear all objects for next frame

#Setup video creation
bpy.data.scenes["Scene"].render.use_sequencer = True
area = bpy.context.area
old_type = area.type
area.type = 'SEQUENCE_EDITOR'
imdir = os.path.join(script_dir, 'temp')
filelist = [{"name":i} for i in os.listdir(imdir)]
n = len(filelist)

#Create the video from frames
a = bpy.ops.sequencer.image_strip_add(
    directory = imdir, files = filelist, channel=1, frame_start=0, frame_end=n-1
    )
out_dir = os.path.join(script_dir, 'output', 'CSG_Dynamics.avi')
stripname=filelist[0].get("name");
bpy.data.scenes["Scene"].frame_end = n
bpy.data.scenes["Scene"].render.image_settings.file_format = 'AVI_JPEG'
bpy.data.scenes["Scene"].render.filepath = out_dir
bpy.ops.render.render( animation=True )

#Cleanup
area.type = old_type
rel_path = 'temp'
abs_file_path = os.path.join(script_dir, rel_path)
shutil.rmtree(abs_file_path)
\end{lstlisting}

\end{document}


The document collection we work with comes directly from webscraping various Czech news servers, and does not have any special structure. The documents consist only of headlines, bodies and publication days. Furthermore, there are some noisy words such as residual HTML entities, typos, words cut in the middle, etc. To make the most of the collection, we preprocess the documents to remove as many of these errors as possible, and also to gain some additional information about the text.

We first employ some NLP (Natural Language Processing) methods to gain insight into the data. Then, we train a model to obtain word embeddings, which we discuss next.

Our event detection method is keyword-based --- the events will be represented by groups of keywords related in the temporal as well as semantic domain. To be able to measure the semantic similarity, we need to obtain a representation of the individual words that retains as much semantic information as possible while supporting similarity queries. There is a number of ways to do so --- a simple TFIDF (Term Frequency-Inverse Document Frequency) representation \citep{tfidf, information-retrieval} which represents the words by weighted counts of their appearance in the document collection. More complicated methods, such as Latent Semantic Indexing \citep{lsi} attempt to discover latent structure within words to also reveal topical relations between them. This idea is further pursued by probabilistic topical models, such as Latent Dirichlet Allocation \citep{lda}.

In this thesis, we use the Word2Vec model introduced by \cite{word2vec, distributed-representations, linguistic-regularities}, which uses a shallow neural network to project the words from a predetermined vocabulary into a vector space. Vectors in this space have interesting semantical properties, such as vector arithmetics preserving semantic relations, or semantically related words forming clusters. A useful property of the Word2Vec model is that it supports online learning, meaning that the training can be stopped and resumed as needed. We can then train the model on one document collection, and only perform small updates when we receive new documents with different vocabulary.

Later on, we will need some sort of word similarity measure. This will come up several times in the course of the thesis --- in the event detection itself, later when querying the document collection to obtain document representation of the events detected, and finally when generating human-readable summaries. The Word2Vec model is fit for all of these uses, as opposed to the other approaches mentioned above, some of which are designed only to measure document similarity, or, on the other hand, do not support document similarity queries very well.

\section{Preprocessing}
Some of the documents contain residual HTML entities from errors during web scraping, which we filter out using a manually constructed stopwords list.

We used the MorphoDiTa tagger \citep{morphodita} to perform tokenization, lemmatization and parts of speech tagging. Our whole analysis is applied to these lemmatized texts; we revert to the full forms only at the end when annotating the events in a human-readable way.

\section{Word embeddings} \label{word-embeddings}
Next, we train the previously mentioned Word2Vec model. Although the training is time-consuming \footnote{See \autoref{chap:evaluation} for computation times.}, the word vectors can be pre-trained on a large document collection and then reused in following runs. In case the vocabulary used in these new documents differs, the model can be simply updated with the new words.

For the training, we only discard punctuation marks and words denoted as unknown parts of speech by the tagger. Such words are mostly typos not important for our analysis. We also discard words appearing in less than 10 documents.

The thesis was implemented using the Gensim \citep{gensim} library. The project contains memory efficient, easy to use Python implementations of various topic modeling algorithms, Word2Vec included. In addition, we used the SciPy toolkit \citep{scipy} and Scikit-Learn \citep{scikit-learn} for various machine learning-related computations.

We use the skip-gram model defined in \cite{word2vec}, which was shown in \cite{distributed-representations} to learn high quality word embeddings well capturing semantic properties. After experimenting with different settings on a smaller subset of the documents, we decided to embed the words in a 100-dimensional vector space and to allow 5 passes over the document collection. Allowing more passes slows down the training, while not improving the quality very much. Setting higher dimensionality also does not lead to significant quality improvement, and slows down the training as well as requires more memory.

In the thesis, we refer to the vector embedding of a word $w$ as $\embed_{w} \in \R^{100}$.

\section{Document collection}
The dataset used is a collection of Czech news documents from various sources accumulated over a period from January 1, 2014 to January 31, 2015. The collection contains 2,078,774 documents averaging at 260 words each, with 2,058,316 unique word tokens in total. However, majority of the words are rare words or typos of no importance, so the number of unique real words is much lower. This is confirmed after discarding the words appearing in less than 10 documents, with only 351,136 unique words remaining.

These words are further processed in the following chapter, where we uncover a small subset of words possibly representative of an event, and discard the rest.

\section{Document stream formally}
Formally, the input to the algorithm is a collection of $\doccount$ news documents containing full text articles along with their publication days and headlines.

If we denote $t_{i}$ as the publication day of a document $d_{i}$, the collection can be understood as a stream $\left\{ (d_{1}, t_{1}), (d_{2}, t_{2}), \dots, (d_{\doccount}, t_{\doccount}) \right\}$ with $t_{i} \leq t_{j}$ for $i < j$. Furthermore, we define $\streamlen$ to be the length of the stream (in days), and we normalize the document publication days to be relative to the document stream start; that is $t_{1} = 1$ and $t_{\doccount} = \streamlen$.


\subsection{Mint}
\label{sec:721Mint}

Suppose Alice wishes to be able to transfer ownership of an ERC-721 token under zero-knowledge, so that the following become private:

\begin{center}
  \begin{framed}
    \begin{enumerate}
      \item All details of the ERC-721 token (the `asset').
      \item The identity of the sender of the token (`Alice').
      \item The identity of the recipient of the token.
    \end{enumerate}
  \end{framed}
\end{center}

In order to achieve this, Alice must first convert her ERC-721 token into a private ERC-721 commitment. We call this act of conversion \textbf{`minting'} an ERC-721 commitment.
In this section, we outline Nightfall's protocol for minting an ERC-721 commitment, but first, an important privacy warning:

\begin{center}
  \begin{mdframed}[backgroundcolor=verylightred]
    \noindent
    PRIVACY WARNING\\
    \\
    \textit{
    Privacy is NOT achieved during the minting stage!
    }\\
    \\
    Minting an ERC-721 commitment initially requires Alice to transfer her ERC-721 token to a `Shield' contract (which thereafter holds it in escrow). This transfer reveals the Ethereum address of the sender (Alice) as well as the ERC-721 token itself. Therefore everyone will know the owner and the underlying asset being represented by the initial ERC-721 commitment which is created at this `minting' stage.\\
    \\
    Only during subsequent `transfers' of the new ERC-721 commitment, will we achieve the privacy intentions of Figure~\ref{fig:nfPrivacyIntentions}
  \end{mdframed}
\end{center}
\ \\
The ERC-721 standard allows many unique assets to be tokenised and represented by a unique tokenId within an ERC-721 smart contract.
Let $\alpha$ be the tokenId of some ERC-721 asset.\\
\\
For Alice to mint a token commitment representing $\alpha$, on the blockchain, under zero knowledge, she follows the steps in Figure~\ref{fig:nfMintAlgorithm}:

\begin{figure}[hp]
	\begin{center}
		\begin{framed}
      \begin{tabular}{p{16cm}}
        \textbf{Non-fungible mint algorithm} \\
        \midrule
        \textbf{Alice's steps:}\\
        \begin{enumerate}
				  \item Generate a random salt $\sigma_{A}$.
				  \item Compute $Z_A := h(\;\alpha\;|\;pk^Z_A\;|\;\sigma_{A}\;)$, a token commitment which represents $\alpha$.
          \item Set public inputs $x = (\;\alpha,\;Z_A\;)$
          \item Set private inputs $\omega = (\;pk_A^Z,\;\sigma_{A}\;)$
          \item Select $C_{nft\mbox{-}mint}(\;\omega,\;x\;)$ -- the set of constraints which are satisfied if and only if:
          \begin{enumerate}
            \item $Z_A$ equals $h(\;\alpha\;|\;pk_A^Z\;|\;\sigma_A\;)$ (Proof that the commitment $Z_A$ hides the correct asset $\alpha$)
          \end{enumerate}
          \item Generate $\pi := P(\;p_C\;,\;x,\;\omega\;)$; a proof of knowledge of satisfying arguments $(\omega, x)\;s.t.\;C(\omega, x) = 1$. Recall: $p_C$ -- the proving key for $C$ -- will be stored on Alice's computer.

          The pair $(\pi, x)$ is the zk-SNARK which attests to knowledge of private inputs $\omega$ without revealing them.
          \item Send $(\pi, x)$ to the Shield contract for verification.

          Using web3: \texttt{nfTokenShield.mint(proof, inputs, vkId)}
          %remember where the count (enumi) is up to and store it in ongoingEnumCounter:
          \setcounter{ongoingEnumCounter}{\value{enumi}}
        \end{enumerate}
        \ \\
        \midrule
        \textbf{Shield contract's steps:}\\
        \begin{enumerate}
          %resume counter
          \setcounter{enumi}{\value{ongoingEnumCounter}}
          \item Verify the proof as correct: call a Verifier contract to verify the \texttt{(proof, inputs)} pair against the verification key represented by \texttt{vkId}.
          \setcounter{ongoingEnumCounter}{\value{enumi}}
        \end{enumerate}
        \ \\
        \midrule
       \textbf{ Verifier contract's steps:}\\
        \begin{enumerate}
          \setcounter{enumi}{\value{ongoingEnumCounter}}
          \item Compute \texttt{result = verify(proof, inputs, vkId)}.

          I.e. Verify the \texttt{(proof, inputs)} pair against the verification key.
          \item Return \texttt{result}$\in$\texttt{\{false, true\}} to the Shield contract.
          \setcounter{ongoingEnumCounter}{\value{enumi}}
        \end{enumerate}
        \ \\
        \midrule
        \textbf{Shield contract's steps:}\\
        \begin{enumerate}
          \setcounter{enumi}{\value{ongoingEnumCounter}}
          \item If \texttt{result = false}, revert.
          \item Else:
          \begin{enumerate}
            \item Transfer $\alpha$ (the ERC-721 token with \texttt{tokenId} $=\alpha$), on behalf of Alice, to the Shield Contract. I.e. store $\alpha$ in escrow.
            \item Add $Z_A$ to the next empty leaf of the Merkle Tree.
            \item Recalculate the path to the root of the Merkle Tree from $Z_A$ for future users.
          \end{enumerate}
          \setcounter{ongoingEnumCounter}{\value{enumi}}
        \end{enumerate}
        \ \\
        \midrule
        \textbf{Alice's steps:}\\
        \begin{enumerate}
          \setcounter{enumi}{\value{ongoingEnumCounter}}
          \item Store relevant data in local database, including the leafindex of $Z_A$.
          \setcounter{ongoingEnumCounter}{0} %reset for next figure
        \end{enumerate}
			\end{tabular}
		\end{framed}
	\end{center}
\caption{Non-Fungible Mint Algorithm}
\label{fig:nfMintAlgorithm}
\end{figure}

\newpage
\subsubsection{Details}
\label{sec:721MintDetails}

We refer to the numbered steps of Figure~\ref{fig:nfMintAlgorithm}.\\
\\
\textbf{Step $1$}
\ \\
This is handled within the \hyperref[sec:ui]{UI} microservice (or within the api-gateway).\\
\\
\textbf{Steps $2 - 4$}
\ \\
These steps are handled within \hyperref[sec:nf-token-controller]{\texttt{nf-token-controller.js}}.\\
\\
\textbf{Steps $5 - 6$}
\ \\
These steps are handled within a \hyperref[sec:zokrates]{ZoKrates} container.\\
\\
\textbf{Step $7$}
\ \\
This transaction is handled within \hyperref[sec:nf-token-zkp]{\texttt{nf-token-zkp.js}}.\\
\\
\textbf{Steps $8 - 10$}
\ \\
The Verifier contract already has stored within it the object $vk_C$ (see \hyperref[sec:trustedSetup]{Trusted Setup}). It runs a verification function $V(vk_C, \pi , x)$.
\begin{align*}
  V: (vk_C, \pi_{C,x,\omega}, x) \to \{0,1\}
\end{align*}
where:
\[
    V=
\begin{cases}
    1,& \text{if } \pi_{C,x,\omega} \text{ and } x \text{ satisfy } vk_C\\
    0,& \text{otherwise}
\end{cases}
\]

\textbf{Steps $11 - 12$}
\ \\
If the Verifier contract returns $1$ (\texttt{true}) (verified) to the Shield contract, then the Shield contract will be satisfied with Alice's commitment, and will update its persistent states:\\
\\
Suppose the Shield contract stores an ever-increasing array, $\bm{Z}$, of all token commitments which have ever been submitted by anyone.\\
\\
Suppose, prior to Alice's mint, there are $n-1$ tokens in the tree:
\begin{align*}
  \bm{Z}_{n-1} = (Z_0, Z_1, ..., Z_{n-1})
\end{align*}
The information held within $\bm{Z}_{n-1}$ may be represented by the root hash $\roott_{n-1}$ of a Merkle Tree $M_{n-1}$:\\

\begin{align*}
  \begin{forest}
    [{$\roott_{n-1}:= h\br*{
                      h\br*{
                        h\br*{
                          h\br*{
                            Z_0,Z_1
                          },
                          ...
                        },
                        h\br*{
                          h\br*{
                            Z_{n-1},0
                          },
                          0
                        }
                      },
                      0
                    }
                  $}
      [{$ h\br*{
            h\br*{
              h\br*{
                Z_0,Z_1
              },
              ...
            },
            h\br*{
              h\br*{
                Z_{n-1},0
              },
              0
            }
          }
        $}
        [{$ h\br*{
              h\br*{
                Z_0,Z_1
              },
              ...
            }
          $}
          [{$ h\br*{
                Z_0,Z_1
              }
            $}
            [{$Z_0$}][{$Z_1$}]
          ]
          [...
            [...][...]
          ]
        ]
        [{$ h\br*{
              h\br*{
                Z_{n-1},0
              },
              0
            }
          $}
          [{$ h\br*{
                Z_{n-1},0
              }
            $}
            [{$Z_{n-1}$}][0]
          ]
          [0
            [0][0]
          ]
        ]
      ]
      [0
        [0
          [0
            [0][0]
          ]
          [0
            [0][0]
          ]
        ]
        [0
          [0
            [0][0]
          ]
          [0
            [0][0]
          ]
        ]
      ]
    ]
  \end{forest}
\end{align*}

Now that the Shield contract has been given verification that Alice's commitment, $Z_A$, does indeed hide the asset $\alpha$, the Shield contract will do the following:\\

\begin{itemize}
  \item Append the commitment $Z_A$ to the ever-increasing array of tokens, $\bm{Z}_{n-1}$, so that $\bm{Z}_{n}=(Z_0, Z_1, ... Z_{n-1}, Z_A)$
  \item Recalculate a Merkle Root $\roott_{n}$ of $M_{n}$:
  \\
  \begin{align*}
    \begin{forest}
      [{$\roott_{n}:= h\br*{
                      h\br*{
                        h\br*{
                          h\br*{
                            Z_0,Z_1
                          },
                          ...
                        },
                        h\br*{
                          h\br*{
                            Z_{n-1},Z_A
                          },
                          0
                        }
                      },
                      0
                    }
                  $}
        [{$ h\br*{
              h\br*{
                h\br*{
                  Z_0,Z_1
                },
                ...
              },
              h\br*{
                h\br*{
                  Z_{n-1},Z_A
                },
                0
              }
            }
          $}
          [{$ h\br*{
                h\br*{
                  Z_0,Z_1
                },
                ...
              }
            $}
            [{$ h\br*{
                  Z_0,Z_1
                }
              $}
              [{$Z_0$}][{$Z_1$}]
            ]
            [...
              [...][...]
            ]
          ]
          [{$ h\br*{
                h\br*{
                  Z_{n-1},Z_A
                },
                0
              }
            $}
            [{$ h\br*{
                  Z_{n-1},Z_A
                }
              $}
              [{$Z_{n-1}$}][{$Z_A$}]
            ]
            [0
              [0][0]
            ]
          ]
        ]
        [0
          [0
            [0
              [0][0]
            ]
            [0
              [0][0]
            ]
          ]
          [0
            [0
              [0][0]
            ]
            [0
              [0][0]
            ]
          ]
        ]
      ]
    \end{forest}
  \end{align*}

  \item Append $\roott_{n}$ to an ever-increasing array $\rootsList=(\roott_0, \roott_1,..., \roott_{n-1}, \roott_{n})$
\end{itemize}
\ \\

\textbf{Step $13$}
\ \\
Alice will store all important information in her private database.


\section{Conclusion}

The encoder makes use of the arithmetic coder to reach decent compression ratios, with significant improvements over the initial RLE-Gb scheme. Because it tries all the possible schemes, it allows for the best available compression regardless of the image size.

To go one step further, it could have been possible to try an adaptive arithmetic encoder. The compression of the data would be less ideal than with the one that was implemented (that relies on the knowledge of the probability of each symbol) but would reduce the size of the necessary dictionary.


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

\section{Conformal decomposition of 3+1 GR}

\marginpar{Tuesday\\ 2021-5-18, \\ compiled \\ \today}

Lichnerowicz in 1944 already used this to deal with the initial data problem.

York in 1971 showed that the dynamical degrees of freedom of GR (GWs) are encoded in the conformal equivalence class of the three-metric:
%
\begin{align}
\gamma_{ij} = \psi^{4} \widetilde{\gamma}_{ij}
\,,
\end{align}
%
which can be shown using ADM + the Cotton-York tensor.
As an example, if \(\widetilde{\gamma}_{ij}\) is the flat 3-metric, we can directly say that there are no GW.

A common choice for conformal metrics is to take the uni-determinant ones: \(\det \gamma_{ij} = \gamma = 1\).
Now, we know that \(\det (cA) = c^{m} \det A\), therefore
%
\begin{align}
\det \gamma_{ij} = \gamma = \psi^{12} \widetilde{\gamma}
\,.
\end{align}

Since \(\widetilde{\gamma} = 1\), this means that \(\psi = \gamma^{1/12}\).

With this choice, \(\psi \) is not a scalar!
Therefore, \(\widetilde{\gamma}_{ij}\) is also not a tensor but instead a tensor density, and it has no Levi-Civita connection associated to it.

The solution to this issue is to introduce an unphysical background three-metric on \(\Sigma_{t}\), so that
\begin{enumerate}
    \item \(f_{ij}\) has signature \(+\), \(+\), \(+\) and \(\det f_{ij} = f\);
    \item it is time independent:
    %
    \begin{align}
    \mathscr{L}_{\partial_{t}} f_{ij} = \pdv{f_{ij}}{t} = 0
    \,,
    \end{align}
    %
    \item it has an inverse: \(f^{ij}f_{jk} = \delta^{i}_{k}\);
    \item it has a Levi-Civita connection \(\mathcal{D}_k f_{ij} = 0\), whose raised-index version is \(\mathcal{D}^{i} = f^{ij} \mathcal{D}_{j}\);
    \item its Christoffel symbols are
    %
    \begin{align}
    F_{ij}{}^{k} = \frac{1}{2} f^{kl} \qty(\partial_{i} f_{lk} +\partial_{j} f_{il} - \partial_{l} f_{ij})
    \,,
    \end{align}
    %
\end{enumerate}

Now \(\psi = (\gamma / f)^{1/12}\) is a scalar. The conformal metric will be
%
\begin{align}
\widetilde{\gamma}_{ij} = \psi^{-4} \gamma_{ij}
\qquad \text{and} \qquad
\widetilde{\gamma}^{ij} = \psi^{4} \gamma^{ij}
\,,
\end{align}
%
and its determinant will be \(\widetilde{\gamma} = f\).

This conformal metric is a ``good'' metric.
What do we use as this metric?
We can take \(f_{ij} = \mathbb{1}_3\), which has vanishing Christoffel symbols; if we are in Cartesian coordinates this is the natural choice.

If, instead, we are in spherical coordinates we might want to use \(f_{ij} = \diag{1, r^2, r^2 \sin^2\theta }\), which has \(f = r^{4} \sin^2 \theta \).

The idea is to take the simplest flat metric in the coordinates we are using.

In the weak-field limit, we have \(\psi^{4} = (1-2 \phi )\), so \(\psi \approx 1 - \phi /2\).
The conformal factor is heuristically analogous to the Newtonian potential.

As an example, the Schwarzschild metric in isotropic coordinates can be written as
%
\begin{align}
\dd{s^2} = - \frac{(1 - M / 2r)^2}{(1 + M / 2r)^2} \dd{t^2}
+ \underbrace{\qty(1 + \frac{M}{2r})^{4}}_{\psi^{4}} \underbrace{\qty(\dd{r^2} + r^2 \dd{\Omega^2})}_{f_{ij}}
\,.
\end{align}

Now we have to define the \textbf{conformal connection and the Ricci tensor}, and specifically the relationship between \(\mathcal{D}_i\) and \(\widetilde{\mathcal{D}}_i\), which both live on \(\Sigma _t\).

The expression reads:
%
\begin{align}
\mathcal{D}_j T_{\dots}^{\dots} \sim \widetilde{\mathcal{D}}_j T_{\dots}^{\dots} + \Sigma C T_{\dots}^{\dots} - \Sigma C T_{\dots}^{\dots}
\,,
\end{align}
%
where \(C^{k}_{ij} = \Gamma^{k}_{ij} - \widetilde{\Gamma}^{k}_{ij}\).
Using this, the Riccis are related by
%
\begin{align}
R_{ij} &= \widetilde{R}_{ij} + \mathcal{\widetilde{D}}C - \widetilde{\mathcal{D}} C + CC - CC  \\
&= \widetilde{R}_{ij} + R^{\psi }_{ij}
\,,
\end{align}
%
where \(R^{\psi }_{ij}\) is a function which contains second covariant derivatives of the conformal factor, something like \(\widetilde{D} \widetilde{D} \psi + \widetilde{D} \psi \widetilde{D} \psi \).

The Ricci scalar, on the other hand, reads
%
\begin{align}
R = \gamma^{ij} R_{ij} = \dots = \psi^{-4} \widetilde{R} - 8 \psi^{-5} \widetilde{D}_i \widetilde{D}^{i} \psi
\,.
\end{align}

\subsection{CD of extrinsic curvature}

We start by performing a trace-traceless decomposition:
%
\begin{align}
K_{ij} = A_{ij} + \frac{1}{3} K \gamma_{ij}
\,.
\end{align}

Then, we decompose the traceless part \(A^{ij}\): \(A^{ij} = \psi^{p} \overline{A}^{ij}\), and \(\overline{A}\) is called the conformal
traceless part of \(K^{ij}\).

There are two possible choices for \(p\): sometimes we take \(p = -4\), which is based on \(\mathscr{L}_m \gamma_{ij}\), which is useful for evolution schemes, and the hyperbolic part of the ADMY equations.

The alternative is \(p = -10\), based on the constraint equations \(C^{i}= 0\).

For the first choice, we take the kinematic equation for \(K_{ij}\), and compute
%
\begin{align}
\mathscr{L}_m \qty(\psi^{4} \widetilde{\gamma}_{ij}) &= - 2 \alpha A_{ij} - \frac{2}{3} \alpha K \psi^{4} \widetilde{\gamma}_{ij}  \\
\psi^{4} \mathscr{L}_m \widetilde{\gamma}_{ij} &= - 4 \psi^{-5} \psi^3 \mathscr{L}_m \widetilde{\gamma}_{ij} - 2 \alpha \psi^{-4} A_{ij} - \frac{2}{3} \alpha K \psi^{-4} \psi^{4} \widetilde{\gamma}_{ij}
\,.
\end{align}

We trace this equation with \(\widetilde{\gamma}^{ij}\), and get
%
\begin{align}
\widetilde{\gamma}^{ij} \mathscr{L}_m \widetilde{\gamma}_{ij} &= - 4 \underbrace{\widetilde{\gamma}^{ij} \widetilde{\gamma}_{ij}}_{=3} \mathscr{L}_m \log \psi
- 2 \alpha \psi^{-4} \widetilde{\gamma}^{ij} A_{ij} - \frac{2}{3} \alpha K \widetilde{\gamma}^{ij} \widetilde{\gamma}_{ij}  \\
&= - 12 \mathscr{L}_m \log \psi - 2 \alpha K
\,.
\end{align}

Alternatively, we can write
%
\begin{align}
\widetilde{\gamma}^{ij} \mathscr{L}_m \widetilde{\gamma}_{ij} &\sim \Tr \qty(M^{-1} \delta M) = \delta (\log \det M)  \\
&= \mathscr{L}_m \log \widetilde{\gamma} = \qty(\partial_{t} \mathscr{L}_\beta ) \log f  = - \mathscr{L}_\beta \log f   \\
&= - \widetilde{\gamma}^{ij} \mathscr{L}_\beta \widetilde{\gamma}_{ij}
= - \widetilde{\gamma}^{ij} \qty(\beta^{k} \widetilde{D}_{k} \widetilde{\gamma}_{ij} + 2 \widetilde{\gamma}_{k (i} \widetilde{D}_{j)} \beta^{k})  \\
&= -2 \widetilde{D}_{i} \beta^{i}
\,.
\end{align}

Both ways, we see that
%
\begin{align}
6 \mathscr{L}_m \log \psi + \alpha K &= \widetilde{D}_i \beta^{i}
\,,
\end{align}
%
so we have conformally decomposed the kinematic equation \(\mathscr{L}_m \gamma = - 2 \alpha K\).
We get
%
\begin{align}
\mathscr{L}_m \log \psi &= \frac{1}{6} \widetilde{D}_{i} \beta^{i}  - \frac{1}{6} \alpha K  \\
\mathscr{L}_m \widetilde{\gamma}_{ij} &= - 2 \alpha \psi^{-4} A_{ij} - \frac{2}{3} \widetilde{D}_{k} \beta^{k} \widetilde{\gamma}_{ij}
\,.
\end{align}

A natural choice which simplifies this is \(p=-4\), since it simplifies \(\psi^{-4} A_{ij} \to \widetilde{A}_{ij}\).
This is commonly denoted as \(\overline{A}\).

The second choice yields equations in the foorm
%
\begin{align}
D_j A^{ij} &= \widetilde{D}_{j} A^{ij} + C^{i}_{jk} A^{kj} + C^{i}_{jk} A^{ik} = \dots  \\
&= \psi^{-10} \widetilde{D}_{j} \qty(\psi^{10} A^{ij})
\,.
\end{align}

This is commonly denoted as \(\hat{A}\).

With this choice we find
%
\begin{align}
0 = C^{i} = \widetilde{D}_{j} \hat{A}^{ij}
- \frac{2}{3} \psi^{6} \widetilde{D}^{i} K
- 8 \pi \psi^{10} p^{i}
\,.
\end{align}

The Hamiltonian constraint, on the other hand, reads
%
\begin{align}
0 = C^{0} = R + K^2 - K_{ij} K^{ij} - 16 \pi E
\,.
\end{align}

This \(K\) is already a conformal variable!
As for the other term, we have
%
\begin{align}
K_{ij} K^{ij} &= \qty(A_{ij} + \frac{1}{2} K \gamma_{ij})
\qty(A^{ij} + \frac{1}{2} K \gamma^{ij})  \\
&= A_{ij} A^{ij} + \underbrace{\frac{2}{3} K \gamma_{ij} A^{ij}}_{= 0} + \frac{1}{3} K^2
\,.
\end{align}

Putting this together yields, for \(p = -4\),
%
\begin{align}
0 = C^{0} = \widetilde{D}_{i} \widetilde{D}^{i} \psi
- \frac{1}{6} \widetilde{R} \psi +
\qty( \frac{1}{8} \widetilde{A}_{ij} \widetilde{A}^{ij} - \frac{1}{12} K^2 + 2 \pi E) \psi^{5}
\,,
\end{align}
%
and for \(p = -10\):
%
\begin{align}
0 = C^{0} = \widetilde{D}_{i} \widetilde{D}^{i} \psi - \frac{1}{6}
\widetilde{R} \psi + \frac{1}{8} \hat{A}_{ij} \hat{A}^{ij} \psi^{-7}
+ \qty( 2 \pi E - \frac{1}{12} K^2 )\psi^{5}
\,.
\end{align}

The latter is called the \textbf{Lichnerovicz equation}: the Hamiltonian constraint in conformal variables.
This is a complicated nonlinear equation, but under certain prescriptions we can make the \(\widetilde{D}_{i} \widetilde{D}^{i}\) term look like a Laplacian.

The other CD ADMY equations are the dynamical ones, we have one for the evolution of \(K\), and one for the evolution of \(\widetilde{A}_{ij}\).
See the notes for more details on these equations.

\end{document}


\subsection{\texorpdfstring{The 1\textsuperscript{st} Law of Thermodynamics Solutions}{The 1st Law of Thermodynamics Solutions}}
\begin{enumerate}
    \item
    \begin{enumerate}
    \item Rearranging equation \ref{eqn:(33)}, to solve for pressure, we have:
    \begin{gather*}
        \left(P+a\frac{n^2}{V^2}\right)V = nRT \\
        PV + a\frac{n^2}{V} = nRT \\
        PV = nRT - \frac{an^2}{V} \\
        P = \frac{nRT}{V}-\frac{an^2}{V^2}
    \end{gather*}
    \item Applying equation \ref{eqn:(16)} (the equation for thermodynamic work), we have:
    \[W = -\int_{V_1}^{V_2}P(V)dV = -\int_{V_1}^{V_2}\left(\frac{nRT}{V}-\frac{an^2}{V^2} \right)dV \]
    \end{enumerate}
    As the compression is isothermal, $T$ is constant and I can take it out of the integral. The remaining integrals for $V$ are elementary:
    \[W = an^2\int_{V_1}^{V_2}\frac{1}{V^2}dV - nRT\int_{V_1}^{V_2}\frac{1}{V}dV \]
    \[ W = an^2 \left. -\frac{1}{V}\right|_{V_1}^{V_2} + nRT\left. \ln(V)\right|_{V_1}^{V_2} \]
    \[W = an^2\left(\frac{1}{V_1}-\frac{1}{V_2}\right) + nRT\ln\left(\frac{V_2}{V_1}\right)\]
    So we see that the work done is very similar to the ideal gas isothermal compression, just with an additional corrective term of $an^2\left(\frac{1}{V_1}-\frac{1}{V_2}\right)$.
    \item Answers summarized in chart below. Note that we define all the signs here in terms of what is done to the gas. So, + work done on gas is when we do work, - work done on gas is when the gas does work for us. + Heat flow into the gas means we supply heat to the gas, - heat flow into the gas means the gas releases heat.
\begin{center}
 \begin{tabular}{|c| c | c | c|}
 \hline
 \textbf{Process} & \textbf{Work on gas} & \textbf{Heat into gas} & $\Delta T$\\
 \hline\hline
 Isochoric heating & 0 & + & + \\
 Isochoric cooling & 0 & - & - \\
 Isobaric expansion & - & + & + \\
 Isobaric compression & + & - & - \\
 Isothermal expansion & - & + & 0 \\
 Isothermal compression & + & - & 0 \\
 Adiabatic expansion & - & 0 & - \\
 Adiabatic compression & + & 0 & + \\
 \hline
 \end{tabular}
 \end{center}
    \item
    \begin{enumerate}
        \item Internal energy is a function of state, and after one cycle the system is back in its initial state. Therefore, the net change in internal energy is zero.
        \item There's an easy way to do this; just calculate the area of the triangle in the graph. So
        \begin{align*}
            W_{net} &= \frac{2\textrm{m}^3\cdot 10\textrm{kPa}}{2}
            = 10\textrm{kJ}
        \end{align*}
        So the work done by the engine in one cycle is $10\textrm{kJ}$.
        \item Since the net change in internal energy of the heat engine is zero, and the heat engine does $10\textrm{kJ}$ of work per cycle, by the first law of thermodynamics, there must be a net flow of $10\textrm{kJ}$ of heat \textit{into} the engine per cycle.
        \item From ideal gas law, we know that $T = \frac{PV}{nR}$. We know that $n=1\textrm{mol}$, and we can just plug in the known values of $P$ and $V$ at each point. For $1$, we have $P = 5\textrm{kPa}, V = 4\textrm{m}^3$, so $T_1 = \frac{5000\textrm{Pa}*4\textrm{m}^3}{1\textrm{mol}*8.314\textrm{ J} \textrm{ mol}^{-1} \textrm{ K}^{-1}} \approx 2,406 \textrm{K}$. Similarly, for $2$ we have $P = 5\textrm{kPa}, V = 2\textrm{m}^3$, giving us $T_2 \approx 1203\textrm{K}$, and for $3$ we have $P = 15\textrm{kPa}, V = 2\textrm{m}^3$, giving us $T_3 \approx 3608\textrm{K}$. This is perhaps an unrealistically hot engine!
        \item Starting with work, we already know that the cycle does a net work of 10kJ on the environment. In process $1 \rightarrow 2$, we can calculate the area under the graph (a nice easy rectangle) to get that the gas does $W = -10\textrm{kJ}$, or we do $10\textrm{kJ}$ of work on the gas. Since process $2 \rightarrow 3$ is isochoric, no work is done. Finally, we can see on the graph that the work done by $3 \rightarrow 1$ is the area of the triangle plus the area of the rectangle. So, this process does $20\textrm{kJ}$ of work on the environment. Adding up the work done during each process, we recover the original net work value of $10\textrm{kJ}$ that the engine does for us. \\
        Now to solve for the heat. For process $1 \rightarrow 2$, we have that $Q = -nc_p\Delta T$ (as we derived for an isobaric process) where $c_p = \frac{\chi}{2}R+R = \frac{5}{2}R$ for a monoatomic gas, and $\Delta T = T_2-T_1 = 2406\mathrm{K} -1203\mathrm{K}  = 1203\mathrm{K} $. Therefore, we have that:
        \[Q_{1\rightarrow 2} = nc_p\Delta T = 1\textrm{mol}*\frac{5}{2}*8.314\textrm{ J} \textrm{ mol}^{-1} \textrm{K}^{-1}*1203\mathrm{K} \approx 25\textrm{kJ}\]
        So we find that 25kJ of heat leaves the engine. \\
        For process $2\rightarrow 3$, we have an isochoric process, so $Q = nc_v\Delta T$, where $c_v = \frac{3}{2}R$ and $\Delta T = T_3-T_2 = 3608\textrm{K}-1202\textrm{K} = 2406\textrm{K}$, so we have that:
        \[Q_{2\rightarrow 3} = nc_v\Delta T = 1\textrm{mol}*\frac{3}{2}*8.314\textrm{ J} \textrm{ mol}^{-1} \textrm{K}^{-1}*2406\mathrm{K} \approx 30\textrm{kJ}\]
        So we find that 30kJ of heat enters the engine. \\
        Finally, for process $3 \rightarrow 1$, we have a bit of a strange process that we haven't defined and doesn't follow a nice rule of something being held constant. In this case, we use the first law of thermodynamics:
        \[ \Delta E = Q+W \]
        and combine it with the relation:
        \[ \Delta E = nc_v\Delta T \]
        To get:
        \[nc_v\Delta T = Q+W\]
        We know that $W = -20\textrm{kJ}$ in this process from the area underneath the curve (the gas does 20kJ of work), and we can solve for $\Delta T = T_1-T_3 = 2406\textrm{K}-3608\textrm{K} = -1,202\textrm{K}$. Plugging everything in, we find:
        \[Q = 1\textrm{mol}*\frac{3}{2}*8.314\textrm{ J} \textrm{ mol}^{-1} \textrm{K}^{-1}*-1202\mathrm{K} + 20\textrm{kJ} \approx 5\textrm{kJ}\]
        So we obtain that 5kJ of heat flows into the engine during this process.
    \end{enumerate}
    \item
    \begin{enumerate}
        \item Since process $3 \rightarrow 1$ is isothermal, $T_1 = T_3$. So, the ratio would be 1:1.
        \item Since $3 \rightarrow 1$ is an isothermal process, we have that $P_1V_1 = P_3V_3$. This can then be rearranged to get that $P_3 = 2P_0$.
        \item Using the same method as in 3) we can see from the graph that point 2 has the lowest temperature. Since $3 \rightarrow 1$ is isothermal, that means that points 3, 1, and any point on the process between are at the highest temperature.
        \item We'll start off by calculating the heat flowing into the engine. Heat must flow into the engine during processes $2 \rightarrow 3$ (since it gains energy with no work done) and $3 \rightarrow 1$ (since it does work but remains at the same energy). Since $2 \rightarrow 3$ is isochoric we can say that
        \begin{align*}
            Q &= \Delta E \\
            &= nc_V\Delta T \\
            &= nc_V\Delta\frac{PV}{nR} \text{  applying ideal gas law} \\
            &= \frac{\chi}{2}V_2(P_3 - P_2) \\
            &= \frac{\chi}{2}\frac{V_0}{2}(2P_0 - P_0) \\
            &= \frac{\chi}{4}V_0P_0
        \end{align*}
        Next, since process $3 \rightarrow 1$ is isothermal we can say that
        \begin{align*}
            Q &= -W \\
            &= -nRT\ln{\frac{V_3}{V_1}} \\
            &= nR\frac{P_1V_1}{nR}\ln{\frac{V_1}{V_3}} \\
            &= P_0V_0\ln{\frac{V_0}{V_0/2}} \\
            &= P_0V_0\ln{2}
        \end{align*}
        So in total we get that $Q_{in} = \frac{\chi}{4}V_0P_0 + P_0V_0\ln{2}$. Next, we'll deal with the net work done by the system. Process $1 \rightarrow 2$ is isobaric, so we can pretty quickly calculate that we do $\frac{P_0V_0}{2}$ joules of work on the system. Process $2 \rightarrow 3$ is isochoric and does no work. Since process $3 \rightarrow 1$ is isothermal, we can say that $W = -Q$. Thus, we get that $P_0V_0\ln{2}$ joules of work is done on the environment. Combining these together to calculate efficiency we get
        \begin{align*}
            \eta &= \frac{W_{net}}{Q_{in}} \\
            &= \frac{P_0V_0\ln{2} - \frac{P_0V_0}{2}}{\frac{\chi}{4}V_0P_0 + P_0V_0\ln{2}} \\
            &= \frac{\ln{2} - \frac{1}{2}}{\frac{\chi}{4} + \ln(2)}
        \end{align*}
        For any monoatomic gas, $\chi = 3$, so we get that $\eta \approx 0.13$.
    \end{enumerate}
    \item
    \begin{enumerate}
        \item The quickest way way to solve it is with process $3 \rightarrow 1$ (although you could also use all three processes, if you so desired). Since this process is adiabatic, we have that
        \begin{gather*}
            T_1V_1^{\gamma-1} = T_3V_3^{\gamma-1} \\
            \frac{T_3}{T_1} = \frac{V_1}{V_3}^{\gamma-1}
        \end{gather*}
        For monoatomic gasses, $\gamma = \frac{5}{3}$ so plugging in all the values
        \begin{equation*}
            \frac{T_3}{T_1} = \frac{V_0}{V_0/2}^{\frac{2}{3}} \\
            \frac{T_3}{T_1} = 2^{\frac{2}{3}}
        \end{equation*}
        \item We're basically doing the same thing here. We can say that
        \begin{gather*}
            P_1V_1^{\gamma} = P_3V_3^{\gamma} \\
            P_3 = P_1\frac{V_1}{V_3}^{\gamma} \\
            P_3 = P_1\frac{V_1}{V_3}^{\frac{5}{3}} \\
            P_3 = P_0 2^{\frac{5}{3}}
        \end{gather*}
        \item Using the same logic as we did for question 3, it's pretty clear on the graph that point 2 has the lowest temperature. We also know from a) that $\frac{T_3}{T_1} > 1$, so we get that point 3 has the highest temperature.
        \item Let's start with the net heat put into the system. Heat only flows into the system in process $2 \rightarrow 3$, so we'll focus on that. In this process no work is done, so we get that $Q = nc_V\Delta T$. The problem is we don't know the temperature in either state. However, we can get tricky with this to rearrange into something more manageable using ideal gas law and the previous parts of this question
        \begin{align*}
            Q &= nc_V(\frac{V_2\Delta P}{nR}) \text{  from ideal gas law} \\
            &= \frac{\chi}{2}V_2\Delta P \\
            &= \frac{\chi}{2}V_2(P_3 - P_2) \\
            &= \frac{\chi}{2}(V_0\cdot 2^{-1})(P_0\cdot 2^{\frac{5}{3}} - P_0\cdot 2^{-1}) \text{  from part b} \\
            &= \frac{\chi}{4}V_0P_0(2^{\frac{8}{3}} - 1)
        \end{align*}
        Ouch. Well no matter, we soldier on to work which is going to be even more difficult. Fun. Let's start with the easy part. Work is only done on the system during process $1 \rightarrow 2$. Since its an isobaric process, this leads us to conclude that $W_{1\rightarrow 2} = \frac{P_0V_0}{2}$. Nice! Now for process $2 \rightarrow 3$, where the system does work (much less nice). We're going to use a similar trick to last time, except our expression for work is coming from adiabatic processes (the negative is placed in front because work on the surroundings is normally considered negative)
        \begin{gather*}
            \Delta E = -W \\
            W = -nC_V\Delta T \\
            W = -nc_V(\frac{\Delta VP}{nR}) \text{  from ideal gas law} \\
            W = -\frac{\chi}{2}(P_1V_1 - P_3V_3) \\
            W = \frac{\chi}{2}(P_0\frac{V_0}{2}2^{\frac{5}{3}} - P_0V_0) \\
            W = \frac{\chi}{2}P_0V_0(2^{\frac{2}{3}} - 1)
        \end{gather*}
        And yet another ooof done. So, we now get that the net work is
        \begin{align*}
            W_{net} &= \frac{\chi}{2}P_0V_0(2^{\frac{2}{3}} - 1) - \frac{P_0V_0}{2} \\
            &= \frac{P_0V_0}{2}(\chi(2^{\frac{2}{3}} - 1) - 1)
        \end{align*}
        We're almost there! Now all that remains is to plug these into the efficiency equation
        \begin{align*}
            \eta &= \frac{W_{net}}{Q_{in}} \\
            &= \frac{\frac{P_0V_0}{2}(\chi(2^{\frac{2}{3}} - 1) - 1)}{\frac{\chi}{4}V_0P_0(2^{\frac{8}{3}} - 1)} \\
            &= 2\frac{\chi(2^{\frac{2}{3}} - 1) - 1}{\chi(2^{\frac{8}{3}} - 1)}
        \end{align*}
        For a monoatomic gas, $\chi = 3$ so we get that $\eta \approx 0.095$. All that for a less efficient process!
    \end{enumerate}
    An interesting thing to note is that the heat engine in 4) is more efficient than the one in 5) only for monoatomic gases. This is of course assuming that diatomic and triatomic gases follow ideal gas law, which isn't a bad approximation (for "low" pressures).
    \item As a quick reminder, we're trying to prove that $\frac{V_2}{V_1} = \frac{V_3}{V_4}$ in our Carnot engine. Since the processes from states d to a and b to c are adiabatic, we have that
    \begin{equation*}
        P_1V_1^\gamma = P_4V_4^\gamma \hspace{2cm} P_2V_2^\gamma = P_3V_3^\gamma
    \end{equation*}
    Since the processes from states a to b and c to d are isothermal, we have that
    \begin{equation*}
        P_1V_1 = P_2V_2 \hspace{2cm} P_3V_3 = P_4V_4
    \end{equation*}
    Since the pressure and volume are both nonzero, we can divide the first two equations to get
    \begin{equation*}
        \frac{P_1V_1^\gamma}{P_2V_2^\gamma} = \frac{P_4V_4^\gamma}{P_3V_3^\gamma}
    \end{equation*}
    Which we can factor to
    \begin{equation*}
        \frac{P_1V_1V_1^{\gamma - 1}}{P_2V_2V_2^{\gamma - 1}} = \frac{P_4V_4V_4^{\gamma - 1}}{P_3V_3V_3^{\gamma - 1}}
    \end{equation*}
    Which, by the third and fourth equations we can see is equivalent to
    \begin{equation*}
        \frac{V_1^{\gamma - 1}}{V_2^{\gamma - 1}} = \frac{V_4^{\gamma - 1}}{V_3^{\gamma - 1}}
    \end{equation*}
    We know that $\gamma > 1$, so we can take the $(\gamma - 1)$th root of both sides to get
    \begin{equation*}
        \frac{V_1}{V_2} = \frac{V_4}{V_3}
    \end{equation*}
    Or, inverting the equation
    \begin{equation*}
        \frac{V_2}{V_1} = \frac{V_3}{V_4}
    \end{equation*}
\end{enumerate}


\section{Syntax}
\label{syn}
\texttt{cplint} permits the definition of discrete probability distributions and continuous probaility
densities.
\subsection{Discrete Probability Distributions}
\label{discrete}
LPAD and CP-logic programs consist of a set of annotated disjunctive clauses.
Disjunction in the head is represented with a semicolon and atoms in the head are separated from probabilities by a colon. For the rest, the usual syntax of Prolog is used.
A general CP-logic clause has the form
\begin{verbatim}
h1:p1 ; ... ; hn:pn :- Body.
\end{verbatim}
where \verb|Body| is a conjunction of goals as in Prolog.
 No parentheses are necessary. The \texttt{pi} are numeric expressions. It is up to the user to ensure that the numeric expressions are legal, i.e. that they sum up to less than one.

If the clause has an empty body, it can be represented like this
\begin{verbatim}
h1:p1 ; ... ; hn:pn.
\end{verbatim}
If the clause has a single head with probability 1, the annotation can be omitted and the clause takes the form of a normal prolog clause, i.e.
\begin{verbatim}
h1 :- Body.
\end{verbatim}
stands for
\begin{verbatim}
h1:1 :- Body.
\end{verbatim}
The coin example of  \cite{VenVer04-ICLP04-IC} is represented as (file \href{http://cplint.eu/e/coin.pl}{\texttt{coin.pl}})
\begin{verbatim}
heads(Coin):1/2 ; tails(Coin):1/2 :-
  toss(Coin),\+biased(Coin).

heads(Coin):0.6 ; tails(Coin):0.4 :-
  toss(Coin),biased(Coin).

fair(Coin):0.9 ; biased(Coin):0.1.

toss(coin).
\end{verbatim}
The first clause states that if we toss a coin that is not biased it has equal probability of landing heads and tails. The second states that if the coin is biased it has a slightly higher probability of landing heads. The third states that the coin is fair with probability 0.9 and biased with probability 0.1 and the last clause states that we toss a coin with certainty.

Moreover, the bodies of rules may contain built-in predicates, predicates
from the libraries \verb|lists|, \verb|apply| and \verb|clpr/nf_r|
plus the predicate
\begin{verbatim}
average/2
\end{verbatim}
that, given a list of numbers, computes its arithmetic mean.

The body of rules may also contain the predicate \verb|prob/2| that computes the
probability of an atom, thus allowing nested probability computations.
For example (\href{http://cplint.eu/e/meta.pl}{\texttt{meta.pl}})
\begin{verbatim}
a:0.2:-
  prob(b,P),
  P>0.2.
\end{verbatim}
is a valid rule.

Moreover, the probabilistic annotations can be variables, as in
(\href{http://cplint.eu/e/flexprob.pl}{\texttt{flexprob.pl}})
\begin{verbatim}
red(Prob):Prob.

draw_red(R, G):-
  Prob is R/(R + G),
  red(Prob).
\end{verbatim}
Variables in probabilistic annotations must be ground when resolution reaches the end of the body,
otherwise an exception is raised.

Alternative ways of specifying probability distribution include
\begin{verbatim}
A:discrete(Var,D):-Body.
\end{verbatim}
or
\begin{verbatim}
A:finite(Var,D):-Body.
\end{verbatim}
where \verb|A| is an atom containg variable \verb|Var| and \verb|D|
is a list of couples \verb|Value:Prob| assigning probability \verb|Prob|
to \verb|Value|.
Moreover, you can use
\begin{verbatim}
A:uniform(Var,D):-Body.
\end{verbatim}
where \verb|A| is an atom containg variable \verb|Var| and \verb|D|
is a list of values each taking the same probability (1 over the length
of \verb|D|).
\subsubsection{ProbLog Syntax}
You can also use ProbLog \cite{DBLP:conf/ijcai/RaedtKT07} syntax, so a general clause takes the form
\begin{verbatim}
p1::h1 ; ... ; pn::hn :- Body
\end{verbatim}
where the \texttt{pi} are numeric expressions.

\subsubsection{PRISM Syntax}
You can also use PRISM \cite{DBLP:conf/ijcai/SatoK97} syntax, so a program is composed of
a set of regular Prolog rules whose body may contain calls to the \verb|msw/2| predicate (multi-ary
switch). A call \verb|msw(term,value)| means that a random variable associated to \verb|term|
assumes value \verb|value|. The admissible values  for a discrete random variable are
specified using facts for the \verb|values/2| predicate of the form
\begin{verbatim}
values(T,L).
\end{verbatim}
where \verb|T| is a term (possibly containing variables) and \verb|L| is a list of values.
The distribution over values is specified using directives for \verb|set_sw/2| of the form
\begin{verbatim}
:- set_sw(T,LP).
\end{verbatim}
where \verb|T| is a term (possibly containing variables) and \verb|LP| is a list of
probability values.
Remember that usually in PRISM each call to \verb|msw/2| refers to a different random
variable, i.e., no memoing is performed, differently from the case of LPAD/CP-Logic/ProbLog.
This behavior can be changed with the setting \verb|prism_memoization|: if set to \verb|true| then
memoization is performed. Its default value is \verb|false|, i.e., no memoization.

For example, the coin example above in PRISM syntax becomes
(\href{http://cplint.eu/e/coinmsw.pl}{\texttt{coinmsw.pl}})
\begin{verbatim}
values(throw(_),[heads,tails]).
:- set_sw(throw(fair),[0.5,0.5]).
:- set_sw(throw(biased),[0.6,0.4]).
values(fairness,[fair,biased]).
:- set_sw(fairness,[0.9,0.1]).
res(Coin,R):- toss(Coin),fairness(Coin,Fairness),msw(throw(Fairness),R).
fairness(_Coin,Fairness) :- msw(fairness,Fairness).
toss(coin).
\end{verbatim}
\subsection{Continuous Probability Densities}
\label{cont}

\verb|cplint| handles continuous or integer random variables as well with its
sampling inference module.
To specify a probability density on an argument \verb|Var| of an atom
\verb|A| you can used rules of the form
\begin{verbatim}
A:Density:- Body
\end{verbatim}
where \verb|Density| is a special atom identifying a probability  density on variable \verb|Var| and \verb|Body| (optional) is a regular clause body.
Allowed \verb|Density| atoms are
\begin{itemize}
\item \verb|uniform(Var,L,U)|: \verb|Var| is uniformly distributed in $[L,U]$
\item \verb|gaussian(Var,Mean,Variance)|: \verb|Var| follows a Gaussian distribution with mean \verb|Mean| and variance \verb|Variance|. The distribution can be multivariate if \verb|Mean|
is a list and \verb|Variance| a list of lists representing the mean vector and the covariance matrix. In this case the values of \verb|Var| are lists of real values with the same length as
that of \verb|Mean|
\item \verb|dirichlet(Var,Par)|: \verb|Var| is a list of real
numbers following a Dirichlet distribution with $\alpha$ parameters specified
by the list \verb|Par|
\item \verb|gamma(Var,Shape,Scale)|  \verb|Var| follows a gamma distribution
with shape parameter \verb|Shape| and scale parameter \verb|Scale|.
\item \verb|beta(Var,Alpha,Beta)|  \verb|Var| follows a beta distribution
with parameters \verb|Alpha| and \verb|Beta|.
\item \verb|poisson(Var,Lambda)|  \verb|Var| follows a Poisson distribution
with parameter \verb|Lambda| (rate).
\item \verb|binomial(Var,N,P)|  \verb|Var| follows a binomial distribution
with parameters \verb|N| (number of trials) and \verb|P| (success probability).
\item \verb|geometric(Var,P)|  \verb|Var| follows a geometric distribution
with parameter \verb|P| (success probability).
\item \verb|exponential(Var,Lambda)|  \verb|Var| follows an exponential distribution
with parameter \verb|Lambda| (rate, or inverse scale).
\item \verb|pascal(Var,R,P)|  \verb|Var| follows an exponential distribution
with parameters \verb|R| (number of failures) and  \verb|P| (success probability).
\end{itemize}
For example
\begin{verbatim}
g(X): gaussian(X,0, 1).
\end{verbatim}
states that argument \verb|X| of \verb|g(X)| follows a Gaussian
distribution with mean 0 and variance 1, while
\begin{verbatim}
g(X): gaussian(X,[0,0], [[1,0],[0,1]]).
\end{verbatim}
states that argument \verb|X| of \verb|g(X)| follows a Gaussian
multivariate distribution with mean vector $[0,0]$ and covariance matrix
$$\left[\begin{array}{rr}
1&0\\
0&1
\end{array}\right]$$.

For example, \href{http://cplint.eu/e/gaussian_mixture.pl}{\texttt{gaussian\_mixture.pl}} defines a mixture of two Gaussians:
\begin{verbatim}
heads:0.6;tails:0.4.
g(X): gaussian(X,0, 1).
h(X): gaussian(X,5, 2).
mix(X) :- heads, g(X).
mix(X) :- tails, h(X).
\end{verbatim}
The argument \verb|X| of
\verb|mix(X)| follows a distribution that is a mixture of two Gaussian,
one with mean 0 and variance 1 with probability 0.6 and one with
mean 5 and variance 2 with probability 0.4.

The parameters of the distribution atoms can be taken from the probabilistic
atom, the example \href{http://cplint.eu/e/gauss_mean_est.pl}{\texttt{gauss\_mean\_est.pl}}
\begin{verbatim}
val(I,X) :-
  mean(M),
  val(I,M,X).
mean(M): gaussian(M,1.0, 5.0).
val(_,M,X): gaussian(X,M, 2.0).
\end{verbatim}
states that for an index \verb|I| the continuous variable \verb|X| is
sampled from a Gaussian whose variance is 2 and whose mean is sampled from a Gaussian with mean 1 and
variance 5.

Any operation is allowed on continuous random variables. The example below
(\href{http://cplint.eu/e/kalman_filter.pl}{\texttt{kalman\_filter.pl}}) encodes a Kalman filter:
\begin{verbatim}
kf(N,O, T) :-
  init(S),
  kf_part(0, N, S,O,T).
kf_part(I, N, S,[V|RO], T) :-
  I < N,
  NextI is I+1,
  trans(S,I,NextS),
  emit(NextS,I,V),
  kf_part(NextI, N, NextS,RO, T).
kf_part(N, N, S, [],S).
trans(S,I,NextS) :-
  {NextS =:= E + S},
  trans_err(I,E).
emit(NextS,I,V) :-
  {NextS =:= V+X},
  obs_err(I,X).
init(S):gaussian(S,0,1).
trans_err(_,E):gaussian(E,0,2).
obs_err(_,E):gaussian(E,0,1).
\end{verbatim}
Continuous random variables are involved
in arithmetic expressions (in \verb|trans/3| and \verb|emit/3|). It
is often convenient, as in this case, to use CLP(R) constraints (by
including the directive \verb|:- use_module(library(clpr)).|) as
in this way the expressions can be used in multiple directions and
the same clauses can be used both to sample and to evaluate the weight of the sample on the basis
of evidence,
otherwise different clauses have to be written.
In case random variables are not sufficiently instantiated to
exploit expressions for inferring the values of other variables,
inference will return an error.

Moreover, user defined distributions are allowed with the syntax:
\begin{verbatim}
A:user(Var,Density):-Body.
\end{verbatim}
where \verb|Var| appears in \verb|A| and will contain the sampled value and \verb|Density| is an atom of the form \verb|predicate(Parameters)|.
If  \verb|predicate| in \verb|predicate(Parameters)| has arity \verb|n|, then the user has to define predicate \verb|predicate/n+1| such that \
\begin{verbatim}
predicate(Parameters,Var)
\end{verbatim}
called with \verb|Parameters| instantiated, returns in \verb|Var| a value sampled from the user defined density.
The definition of \verb|predicate/n+1| should appear after \verb|:- end_lpad.|

If likelihood weighting or particle filtering will be used for inference, then the user has to define also predicate \verb|predicate/n+2| such that \verb|predicate(Parameters,Var,Dens)|, when
called  with \verb|Parameters| and \verb|Var| instantiated, returns in \verb|Dens| the value of the probability density of \verb|Var|.
The definition of \verb|predicate/n+2| should appear after \verb|:- end_lpad.|
Moreover, if the density is discrete, the program must include the fact
\begin{verbatim}
disc(predicate).
\end{verbatim}
also  after \verb|:- end_lpad.|

See for example \href{http://cplint.eu/e/gauss_mean_est_user.pl}{\texttt{gauss\_mean\_est\_user.pl}} and
\href{http://cplint.eu/e/binomial_user.pl}{\texttt{binomial\_user.pl}}.

\subsubsection{Distributional Clauses Syntax}
\label{dc}
You can also use the syntax of Distributional Clauses (DC) \cite{Nitti2016}.
Continuous random variables are represented in this case by term whose distribution can be specified with density atoms as in
\begin{verbatim}
T~Density' := Body.
\end{verbatim}
Here \verb|:=| replaces the implication symbol, \verb|T| is a term and \verb|Density'| is one of the density atoms above without the \verb|Var| argument, because \verb|T|
itself represents a random variables. In the body of clauses you can use the infix operator \verb|~=| to equate a term representing a random variable with a logical variable or
a constant as in \verb|T ~= X|. Internally \verb|cplint| transforms the terms representing random variables into atoms with an extra argument for holding the variable.
DC can be used to represent also discrete distributions using
\begin{verbatim}
T~uniform(L) := Body.
T~finite(D) := Body.
\end{verbatim}
where \verb|L| is a list of values and \verb|D| is a list of couples \verb|P:V| with \verb|P| a probability and \verb|V| a value.
If \verb|Body| is empty, as in regular Prolog, the implication symbol \verb|:=| can be omitted.

The Indian GPA problem from \url{http://www.robots.ox.ac.uk/~fwood/anglican/examples/viewer/?worksheet=indian-gpa}in distributional clauses syntax  (\url{https://github.com/davidenitti/DC/blob/master/examples/indian-gpa.pl})
takes the form (\href{http://cplint.eu/e/indian_gpadc.pl}{\texttt{indian\_gpadc.pl}}):
\begin{verbatim}
is_density_A:0.95;is_discrete_A:0.05.
% the probability distribution of GPA scores for American students is
% continuous with probability 0.95 and discrete with probability 0.05

agpa(A): beta(A,8,2) :- is_density_A.
% the GPA of American students follows a beta distribution if the
% distribution is continuous

american_gpa(G) : finite(G,[4.0:0.85,0.0:0.15]) :- is_discrete_A.
% the GPA of American students is 4.0 with probability 0.85 and 0.0
% with
% probability 0.15 if the
% distribution is discrete
american_gpa(A):- agpa(A0), A is A0*4.0.
% the GPA of American students is obtained by rescaling the value of
% agpa
% to the (0.0,4.0) interval
is_density_I : 0.99; is_discrete_I:0.01.
% the probability distribution of GPA scores for Indian students is
% continuous with probability 0.99 and discrete with probability
% 0.01
igpa(I): beta(I,5,5) :- is_density_I.
% the GPA of Indian students follows a beta distribution if the
% distribution is continuous
indian_gpa(I): finite(I,[0.0:0.1,10.0:0.9]):-  is_discrete_I.
% the GPA of Indian students is 10.0 with probability 0.9 and 0.0
% with
% probability 0.1 if the
% distribution is discrete
indian_gpa(I) :- igpa(I0), I is I0*10.0.
% the GPA of Indian students is obtained by rescaling the value
% of igpa
% to the (0.0,4.0) interval
nation(N) : finite(N,[a:0.25,i:0.75]).
% the nation is America with probability 0.25 and India with
% probability 0.75
student_gpa(G):- nation(a),american_gpa(G).
% the GPA of the student is given by american_gpa if the nation is
% America
student_gpa(G) :- nation(i),indian_gpa(G).
% the GPA of the student is given by indian_gpa if the nation
%is India
\end{verbatim}
See


%!TEX root = ../thesis.tex
%*******************************************************************************
%****************************** Fourth Chapter **********************************
%*******************************************************************************
\chapter{Results}

\nomenclature[e-0-N]{$N$}{The number of datasets}

% **************************** Define Graphics Path **************************

\graphicspath{{Chapter4/Figs/Vector/}{Chapter4/Figs/}}
Results are presented for the algorithms discussed in Chapter~\ref{ch:method}. Where possible, errors have been provided by taking the sample standard deviation of the results provided and dividing by $\sqrt{N-1}$. This allows for robust discussion and comparison of each method used. Within figures, lines are added to guide the eye to changes.

\section{Non-Parametric}
Non-parametric equations have the benefit of not requiring the minimisation function. Due to this, all testing of these algorithms were undertaken on a standard laptop. These also tend to be the easiest to implement, as uncovered in Chapter~\ref{ch:method}. Particularly important is the Monte Carlo method as this allows shows what should be a minimum baseline to achieve.

\subsection{Monte Carlo}
The first non-parametrised algorithm discussed in Chapter~\ref{ch:method} was the Monte Carlo method. Due to the non-parametric nature of this algorithm, execution was simply carried out on the test data set. Results are presented in Figure~\ref{fig:MCTestSet}, demonstrating a final score of ${0.184\pm{}0.018}$ and an overall $\dot{\mathrm{score}}$ of $16.32\pm{}0.36$.

\begin{figure}[H]
    \begin{center}
        \includegraphics{dumb1.pdf}
        \caption[Monte Carlo]{Results of Monte Carlo sampling on the test datasets. Dotted lines represent the individual scoring for the datasets and the solid line shows the mean results at each iteration in order to guide the eye.}
        \label{fig:MCTestSet}
    \end{center}
\end{figure}

\subsection{Greed}
Likewise, the Greedy algorithm was tested, with results presented in Figure~\ref{fig:GreedyTestSet}. Here, a final score of ${0.323\pm{}0.039}$ and an overall $\dot{\mathrm{score}}$ of $13.5\pm{}0.8$, indicating a worse performance than the base case.

\begin{figure}[H]
    \begin{center}
        \includegraphics{greedy1.pdf}
        \caption[Greedy]{Results of greedy sampling on the test datasets. Dotted lines represent the individual scoring for the data sets and the solid line shows the mean results at each iteration in order to guide the eye.}
        \label{fig:GreedyTestSet}
    \end{center}
\end{figure}

\subsection[Region of Disagreement]{RoD}
The final non-parametric algorithm to be tested was RoD. A final score of ${0.211\pm{}0.022}$ and an overall $\dot{\mathrm{score}}$ of $15.78\pm{}0.44$, leading to a middling position between the other two parametric algorithms. The improvement in each iteration is shown in Figure~\ref{fig:RODTestSet}.

\begin{figure}[H]
    \begin{center}
        \includegraphics{rod1.pdf}
        \caption[RoD]{Results of RoD sampling on the test datasets. Dotted lines represent the individual scoring for the datasets and the solid line shows the mean results at each iteration in order to guide the eye.}
        \label{fig:RODTestSet}
    \end{center}
\end{figure}

\section{Parametric}
Parametric algorithms require a minimisation procedure on the training set. This is computationally challenging, and for this the author is grateful for the services provided by the HPC \cite{HPC}.

\subsection{Clusters}
The first set of algorithms tested were the clusters. Each of these outperformed all three of the other algorithms, with Cluster I, Cluster II, and Cluster III giving scores of ${0.155\pm{}0.020}$, ${0.145\pm{}0.009}$, and ${0.143\pm{}0.016}$ respectively. This corresponds to $\dot{\mathrm{score}}$ of ${16.90\pm{}0.40}$, ${17.10\pm{}0.18}$, and ${17.14\pm{}0.32}$. Due to the results from Cluster III, this is the one that will be used within the Holy Trinity. A variety of optimal ${c}$ were found when comparing to Algorithm~\ref{alg:cluster1}. An additional cluster size of 45, 40, and 60 were found to be optimal for Cluster I, II, and III respectively.

% \begin{figure}[H]
%   \begin{center}
%     \includegraphics{cluster.pdf}
%   \end{center}
% \end{figure}
% \begin{figure}[H]
%   \begin{center}
%     \includegraphics{clusterII.pdf}
%   \end{center}
% \end{figure}
\begin{figure}[H]
    \begin{center}
        \includegraphics{clusterIII.pdf}
        \caption[Cluster III]{The results of fitting $c$ to the Cluster III algorithm. A) shows the results from parameter fitting and B) shows the learning process on the test set. Dotted lines show performance of individual datasets and solid lines guide the eye to the trend.}
        \label{fig:clusterTest}
    \end{center}
\end{figure}

\subsection[Region of Disagreement with Greed]{RoDG}
When testing the RoDG sampling algorithm, it was found that despite the weighting towards higher value targets in scoring, no significant improvement was seen over RoD. The minimisation procedure returned ${\alpha{}=0.06}$, with $\alpha$ defined in (\ref{eq:rodAndGreed}). However, the tolerance at small $\alpha$, as shown in Figure~\ref{fig:rogreed}A. This method gave a final a score of ${0.206\pm{}0.011}$ and overall $\dot{\mathrm{score}}=15.88\pm{}0.22$, a slight improvement over simply using RoD.

\begin{figure}[H]
    \begin{center}
        \includegraphics{rodGreedParam.pdf}
        \caption[RoD with Greed]{The results of fitting $\alpha{}$ to the RoDG algorithm. A) shows the results from parameter fitting and B) shows the learning process on the test set. Dotted lines show performance of individual datasets and solid lines guide the eye to the trend.}
        \label{fig:rogreed}
    \end{center}
\end{figure}

\subsection[Region of Disagreement with Greed and Clusters]{RoDGer}
By sampling multiple values for $\alpha$, a final set of $\alpha=[60, 0.47, 0.22]$ was reached where $[\alpha{}_0, \alpha{}_1, \alpha{}_2]$ correspond to the constants used in Algorithm~\ref{alg:cluster1}, (\ref{eq:rodAndGreed}), and (\ref{eq:holyTrinity}) respectively. When validated against the testing datasets, a final score of $0.115\pm{}0.013$ was found with an overall $\dot{\mathrm{score}}=17.70\pm{}0.26$; the best result.

\begin{figure}[H]
    \begin{center}
        \includegraphics{holyGrail.pdf}
        \caption[RoDGer Test Results]{The results after fitting $\alpha{}$ to RoDGer, showing the learning process on the test set. The learning of each dataset has been added with dotted lines for illustrative purposes, and solid lines guide the eye to overall trends.}
        \label{fig:holyTrinity}
    \end{center}
\end{figure}