I put a lot of blood sweat and tears into making my code work.  It is available on my github page: \url{github.com/jgoodknight}.  I made heavy use of the numpy and scipy libraries~\cite{scipy,numpy}  There are some details I wish to point out, however, in my thesis and this seems the appropriate place to do so.

\section{Working with Natural Units: or, Why Mess with SI?}

While an experimentalist needs a systems of units he or she can measure, we theorists prefer to have a system of units which is easy to write down, so we can worry less about dropping factors here and there.  Take for example the hydrogen atom Hamiltonian:

\begin{align*}
	\hat{H} = -\frac{\hbar ^2}{2 m_e} \nabla^2 - \frac{1}{4 \pi \epsilon_0} \frac{e^2}{r}
\end{align*}
That is a lot of constants.  Wouldn't it be better if we could just write this out:
\begin{align*}
	\hat{H} = -\frac{1}{2 } \nabla^2 -  \frac{1}{r}
\end{align*}
This easy-to-not-mess-up Hamiltonian can be yours with a little algebra and a bit of thought.

\subsection{Setting Constants equal to 1}
The astute observer will note that one can get the miraculous Hamiltonian Transformation by making the following algebraic declarations:
\begin{align*}
	\hbar &= 1 \\
	e &= 1 \\
	m_e &= 1 \\
	\frac{1}{4 \pi \epsilon_0} = k_e &= 1
\end{align*}
You may now be wondering how they can be 1.  You've probably had many professors talk to you for many hours about the painstaking effort that went into determining the exact values of these constants throughout the history of physics, and here I go, willy-nilly just setting them to one: doesn't this throw off all of physics into non-physical nonsense?  Yes and no.

There is, unsurprisingly, a limit to the number of units you can set to being 1.  This limit is imposed by the fine-structure constant $\alpha$:
\begin{align*}
	\alpha = \frac{k_e e^2}{\hbar c}
\end{align*}
Which, unless you listen to some theoretical cosmologists, is completely constant in our universe and has a value of $^\sim 1/137$.  So in our system of units we have set all but one of these constants which make up the fine-structure constant to be unity (the absolute limit), so we must get out that $c = \frac{1}{\alpha}$ to keep our system of units from describing a universe other than our own.

\subsection{Setting units equal to 1 or: Finding the ``Natural'' units}
The only reason we are able to set these units to be one is because we now measure things like energy, distance and time in units which MAKE the constants one.  $\hbar$ for example is not really just 1; it's 1 atomic-energy-unit-atomic-time-unit or $1 E_a \cdot t_a$.  To figure out exactly what these atomic units are, we do this for each constant noting that energy will be $E_a = m_a l_a^2/t_a^2$:

\begin{align*}
	\hbar &= 1 E_a \cdot t_a = m_a \frac{l_a^2}{t_a}\\
	k_e &= 1 \frac{E_n \cdot l_a}{q_a ^2}
\end{align*}
We've already defined our charge units to be $e$ and our mass units to be $m_e$  (although that can easily change).
\begin{align*}
	\frac{\hbar}{m_e} &= \frac{l_a^2}{t_a}\\
	k_e e^2 &= m_e \frac{l_a^2}{t_a^2} \cdot l_a
\end{align*}
Now with two equations and two unknowns, we begin to solve
\begin{align*}
	\frac{\hbar}{m_e} &= \frac{l_a^2}{t_a}\\
	t_a &= \frac{l_a^2 m_e}{\hbar} \\
	t_a^2 &= \frac{l_a^3 m_e}{k_e e^2} \\
	\frac{l_a^4 m_e^2}{\hbar^2} &= \frac{l_a^3 m_e}{k_e e^2} \\
	l_a &= \frac{\hbar^2}{m_e k_e e^2}
\end{align*}
Which the astute observer will note has an $\alpha$ hidden in there... somewhere:
\begin{align*}
	l_a &= \frac{\hbar}{m_e \alpha c}
\end{align*}
And the even more astute observer will notice that it is the Bohr radius.  This quickly emits the time and then energy units:
\begin{align*}
	1 l_a &= \frac{\hbar}{m_e \alpha c} = a_0 \approx 5.29 \times 10^{-11}\text{ m} \\
	1 t_a &= \frac{\hbar}{m_e \alpha^2 c^2 } \approx 2.419 \times 10^{-17} \text{ s} \\
	1 E_a &= m_e \alpha^2 c^2 \approx 4.3597 \times 10^{-18} \text{ J}
\end{align*}
wavenumbers come up a lot on spectroscopy so let's figure out what the atomic wavenumber unit is:
\begin{align*}
	1 \omega_a &= \frac{1 E_a}{h c} = 219474.6 \text{ cm}^{-1} = \frac{1}{1 l_a} \\
	hc &= 2 \pi \hbar c = \frac{2 \pi}{\alpha} = 861.022576 E_a \cdot l_a
\end{align*}
So to convert a wavenumber $\omega$ into an atomic energy unit:
 \begin{align*}
	E_a (\omega) &= \frac{\omega}{1 \omega_a} \frac{2 \pi}{\alpha} \\
	&= \omega \left( 0.00392310807 \frac{E_a}{\text{cm}^{-1}} \right)
\end{align*}

\subsection{Setting other things equal to 1}
Now let's say you are reading a paper which quotes all of its energy units in wavenumbers, all of its position units in square-root-centimeters and time units in femtoseconds.  How do you make sense of this?

Let's say this particular paper is talking about harmonic oscillators, the Hamiltonian of which looks like so:
\begin{align*}
	\hat{H} = \frac{\hbar^2 p^2}{2 m	} + \frac{1}{2} m \omega^2 (x - x_0)^2
\end{align*}
In the paper, however, the author quotes the Hamiltonian as being thus:

\begin{align*}
	\hat{H} = \frac{p^2}{2} + \frac{1}{2} \omega^2 (x - x_0)^2
\end{align*}
and all energy units are given in terms of wavenumbers.  All-together, these implies 3 unitary unit relationships:
\begin{align*}
	\hbar = 1 \\
	hc = 1 \\
	m = 1
\end{align*}
Luckily, we don't care about coulomb relations so we can just set $c=2 \pi$ and let the electron charge and Coulomb constant float to strange numbers.  Anyway, as we know from earlier, these really do have units associated with them so we add them in, calling them $j$ units.
\begin{align*}
	\hbar = 1 E_j \cdot t_j \\
	m = 1 m_j
\end{align*}
The author has chosen wavenumbers as the units of choice for energy: specifically 100 wavenumbers.
\begin{align*}
	1 E_j &\sim 100.0 \text{ cm}^{-1} \\
	1 E_j &= h c (100.0 \text{ cm}^{-1}) = 1.986 \times 10^{-21} \text{J}
\end{align*}
which also gives us the natural units for time:
\begin{align*}
	1 t_j = \frac{\hbar}{1 E_j} = 5.308837 \times 10^{-12} \text{s} = 5.3 \text{ ps}
\end{align*}

\subsection{Conclusion on Units}
I have shown you the basic derivation for how to get the system of natural units (units with a bunch of constants equal to one)  known as atomic units.  Hopefully you can follow my logic to create any easy-to-derive-with system of units that you desire.  Up for a challenge?  Let the mass and charge float, set $c=1$ and then figure out how to set Boltzmann's constant and the universal gravitational constant equal to 1 also.  These are known as the Planck units and are cool because they set all the fundamental constants of the forces to 1, instead of any property of an object

\section{Discrete Fourier Transform}
Let's say you want a computer to do the following integral
\begin{align}
	\tilde{f} (\omega) = \int e^{i \omega t} f(t) dt
\end{align}
And your t values stretch from $0\rightarrow n \delta t$.  We could then do the integral like a Riemann Sum:
\begin{align}
	\tilde{f} (\omega) = \delta t \sum_{j=0}^{n}  e^{i \omega j \delta t} f( j \delta t )
\end{align}
but how to discretize $\omega$?  I'm going to skip over why but say that Numpy's Fast Fourier Transform algorithm takes a list of function values $\{f_m\}$ and returns:
\begin{align}
	\tilde{f}_k = \sum_{m=0}^{n - 1}  e^{- 2 \pi i \frac{m k}{n}} f_m
\end{align}
which implies we get $d\omega = \frac{ 2 \pi }{n \delta t}$ with $-\frac{n-1}{2} < k < \frac{n-1}{2} $.  But we defined our Fourier transforms in terms of the positive exponent.  We're in luck, however, because numpy's inverse FFT algorithm looks like this:
\begin{align}
	 f_m = \frac{1}{n} \sum_{k=0}^{n - 1}  e^{2 \pi i \frac{m k}{n}} \tilde{f}_k
\end{align}

\section{Choosing the Parameters of Space}
In the course of simulation, we have to figure out a proper value for the box we put our wavefunctions in that doesn't affect the observables.  So we need something big enough in position space and big enough in momentum space (small enough $\Delta x$.  Let's look at a ladder of harmonic oscillators:
\begin{align}
	H_i = \frac{1}{2m_i} \hat{p}^2 + \frac{1}{2} m_i \omega_i^2 \left( x - \bar{x}_i \right)^2
\end{align}
for the sake of simplicity, we'll say that all the oscillators are accessible to each other via transition dipole moments and that oscillator 0 is the leftmost.  The ground state we will say has non-negligible amplitude from $x_0 \pm n \sigma_0$ where $\sigma_0 = \sqrt{\frac{\hbar}{m \omega_i}}$.

When the ground state (or any initial state but we're being simple here) from the 0th state end ups in the oscillator $j$ whose center is furthest away from its center, the center's have distance $\Delta x_{\max} =  \Delta x_{0j} = ( x_j - x_0)$.  Because of the magic of Gaussian wavepackets in harmonic potentials, the wavepacket will distort and swing to the other side of the potential energy surface and settle at the classical turning point intact.  That wavepacket will now have approximate extent of $x_0 + 2 \Delta x_{\max} + n \sigma_0$.  Which, is the furthest out the wavepacket can ever get.  So If we're only \textbf{simulating a one-interaction experiment},
\begin{align}
	x_{\min} &= x_0 - n \sigma_0 \\
	x_{\max} &= x_0 + 2 \Delta x_{\max} + n \sigma_0 \\
	x_{\max} - x_{\min} &= 2 \Delta x_{\max} + 2n \sigma_0
\end{align}
If, however, we are interacting more than once, things get slightly more complicated.  We have to assume that at some point, the wavepacket centered about $x = x_0 + 2 \Delta x_{\max}$ will once again end up in the 0th oscillator.  Which means that it will swing all the way over to center around  $x = x_0 - 2 \Delta x_{\max}$ and thus for more than two interactions:
\begin{align}
	x_{\min} &= x_0 - 2 \Delta x_{\max} - n \sigma_0 \\
	x_{\max} &= x_0 + 2 \Delta x_{\max} + n \sigma_0 \\
	x_{\max} - x_{\min} &= 4 \Delta x_{\max} + 2n \sigma_0
\end{align}

\subsection{Momentum-Space Considerations  }
Alas, if only computers did not require us to simulate things discretely, we would not have to worry about momentum space but they do and thus we do.  We must chose $N$ points to simulate the system on.  When you discrete Fourier transform a function in $x$, you get values of k ranging from roughly $-\frac{\pi}{ dx}$ to $\frac{\pi}{dx}$ which means we must chose a $dx$ which is small enough to study the largest momentum the problem will end up seeing.
\subsection{One-Interaction}
How do we do this?  Well, consider that our rightmost amplitude is at $x = x_0 + 2 \Delta x_{\max} + n \sigma_0$.  For one interaction, the highest momentum we will see is when it gets to the center of the $j$th well.  Thinking classically, that rightmost piece of amplitude will be motionless at the top and then be potential energy-less at the center so we can say:
\begin{align}
	\frac{p_{\max}^2}{2 m_j} = V_j(x_0 + 2 \Delta x_{\max} + n \sigma_0)
\end{align}
Since $p = \hbar k$ and we already know the form of the potential energy surface:
\begin{align}
	\frac{\left( \hbar k_{\max} \right)^2}{2 m_j} &= \frac{1}{2} m_j \omega_j^2 \left(x_0 + 2 \Delta x_{\max} + n \sigma_0 - x_j \right)^2 \\
	k_{\max} &= \frac{1}{\hbar} m_j \omega_j \left( \Delta x_{\max} + n \sigma_0\right) = \frac{\pi}{dx} \\
	dx &= \frac{\pi \hbar}{m_j \omega_j \left( \Delta x_{\max} + n \sigma_0\right)} = \frac{x_{max} - x_{\min}}{N} \\
	\frac{\pi \hbar}{m_j \omega_j \left( \Delta x_{\max} + n \sigma_0\right)} &= \frac{2 \Delta x_{\max} + 2n \sigma_0}{N}	\\
	N &= \frac{2 m_j \omega_j \left( \Delta x_{\max} + n \sigma_0\right)^2 }{\pi \hbar}
\end{align}

\subsection{Two-Interactions}
But what if that wavepacket at it's absolute rightmost position is then suddenly plummeted down to the leftmost 0th potential energy well?  Then there will be a non-trivially larger maximum momentum as it will have much further to travel to get to the center of the potential energy well.
\begin{align}
	\frac{\left( \hbar k_{\max} \right)^2}{2 m_0}  &= V_0(x_0 + 2 \Delta x_{\max} + n \sigma_0) \\
	k_{\max} &= \frac{1}{\hbar} m_0 \omega_0 \left( x_0 + 2 \Delta x_{\max} + n \sigma_0 -x_0 \right) = \frac{\pi}{ dx} \\
	dx &= \frac{\pi \hbar}{ m_0 \omega_0 \left( 2\Delta x_{\max} + n \sigma_0\right)} = \frac{x_{max} - x_{\min}}{N} \\
	\frac{\hbar \pi}{ m_0 \omega_0 \left( 2\Delta x_{\max} + n \sigma_0\right)} &= \frac{4 \Delta x_{\max} + 2n \sigma_0}{N}\\
	N &= \frac{2 m_0 \omega_0 \left(2\Delta x_{\max} + n \sigma_0 \right)^2}{\pi  \hbar}
\end{align}

\subsection{Summarize}
\subsubsection{One-Interaction}
\begin{align}
	x_{\min} &= x_0 - n \sigma_0 \\
	x_{\max} &= x_0 + 2 \Delta x_{\max} + n \sigma_0 \\
	N &= \frac{2 \pi m_j \omega_j \left( \Delta x_{\max} + n \sigma_0\right)^2 }{\hbar} \\
	dx &= \frac{\hbar}{\pi m_j \omega_j \left( \Delta x_{\max} + n \sigma_0\right)}
\end{align}

\subsubsection{Two-Interactions}
\begin{align}
	x_{\min} &= x_0 - 2 \Delta x_{\max} - n \sigma_0 \\
	x_{\max} &= x_0 + 2 \Delta x_{\max} + n \sigma_0 \\
	N &= \frac{2 \pi m_0 \omega_0 \left(2\Delta x_{\max} + n \sigma_0 \right)^2}{\hbar}\\
	dx &= \frac{\hbar}{\pi m_0 \omega_0 \left( 2\Delta x_{\max} + n \sigma_0\right)}
\end{align}

\subsubsection{More-Than-Two-Interactions}
With a third interaction, one must consider the scenario where the wavepacket at its leftmost is promoted to the rightmost potential energy well where it can again go out to the rightmost edge of the $j$th potential energy well giving us a different maximum distance needed and different maximum potential needed.

\section{Applying $U(t)$ Numerically: the Split Operator Method}
Let's say you want to apply an exponential operator to something:
\begin{align*}
	U = e^{-i t \mathcal{H} / \hbar}
\end{align*}
This does not appear to be terribly difficult at first sight if you know how to take the exponential of whatever $\mathcal{H}$ is.  Let's just make life difficult and call $\mathcal{H}$ the Hamiltonian of a quantum system which would make $U$ the time-evolution operator:
\begin{align*}
	U = e^{-i t / \hbar \mathcal{H}(p, x) } = e^{-i t / \hbar (\mathcal{T}(p) + \mathcal{V}(x) ) }
\end{align*}
So now it appears to be rather difficult as the potential energy is best expressed in the position basis whereas the kinetic energy is best expressed in the momentum basis.  We might consider doing this (to make our lives easier, define $\lambda = \frac{- i t}{\hbar}$) to make things simpler:
\begin{align*}
	U &= e^{\lambda (\mathcal{T} + \mathcal{V} ) }  = e^{\lambda \mathcal{T} } e^{\lambda  \mathcal{V} }
\end{align*}
Then we could apply the first bit, Fourier transform, apply the second bit and then inverse Fourier transform back to the original basis.  Is this, correct, though?  Let's look at the Taylor Series for the two.  First the ``correct'' operator:
\begin{align*}
	U &= e^{\lambda (\mathcal{T} + \mathcal{V} ) } \\
	&= \sum_{k=0}^{\infty} \frac{\lambda^k}{k!} (\mathcal{T} + \mathcal{V} )^k \\
	&= 1 + \lambda (\mathcal{T} + \mathcal{V} ) + \frac{\lambda^2}{2}(\mathcal{T}^2 + \mathcal{V}^2  + \mathcal{T}\mathcal{V} + \mathcal{V}\mathcal{T}) \\
	&+ \frac{\lambda^3}{6}(\mathcal{T}^3+ \mathcal{V}^3 + \mathcal{T}\mathcal{V}^2 + \mathcal{T}^2\mathcal{V} + \mathcal{T}\mathcal{V}\mathcal{T} + \mathcal{V}\mathcal{T}^2 + \mathcal{V}\mathcal{T} \mathcal{V} + \mathcal{V}^2\mathcal{T}  ) + O(\lambda^4)
\end{align*}
Now the proposed correction:
\begin{align*}
	U &= e^{\lambda \mathcal{T} } e^{\lambda  \mathcal{V} }  \\
	&= \sum_{k=0}^{\infty} \frac{\lambda^k}{k!} \mathcal{T}^k \sum_{l=0}^{\infty} \frac{\lambda^l}{l!} \mathcal{V}^k \\
	&= 1 + \lambda (\mathcal{T} + \mathcal{V}) + \frac{\lambda^2}{2}(\mathcal{T}^2 + \mathcal{V}^2 + 2 \mathcal{T} \mathcal{V}) + \frac{\lambda^3}{6}(\mathcal{T}^3 + \mathcal{V}^3  + 3\mathcal{T}\mathcal{V}^2 + 3\mathcal{T}^2 \mathcal{V}  ) + O(\lambda^4)
\end{align*}
So because normally you have commutivity $xy = yx$, because the kinetic and potential operators don't necessarily commute, there is an error term here:
\begin{align*}
	E &=  e^{\lambda (\mathcal{T} + \mathcal{V} ) } - e^{\lambda \mathcal{T} } e^{\lambda  \mathcal{V} }  \\
	&= \frac{\lambda^2}{2} ( \mathcal{T} \mathcal{V} - \mathcal{V} \mathcal{T} ) + O(\lambda^3) \\
	&= \frac{\lambda^2}{2} [ \mathcal{T}, \mathcal{V}] + O(\lambda^3)
\end{align*}
So our approximation is good, so long as $\lambda$ is small enough to keep $\lambda^2$ sufficiently small.  This can be accomplished by letting $t=N\Delta t$ and applying the operator for $\Delta t$, $N$ times.

You can get even better accuracy at the cost of more Fourier transforms using certain schemes as shown below.  (I will spare you the proofs of these)
\begin{align*}
	U &\approx e^{\lambda \mathcal{T} / 2 } e^{\lambda  \mathcal{V} } e^{\lambda \mathcal{T} / 2 } + O(\lambda^3) \\
	U &\approx \left(  e^{\gamma \lambda \mathcal{T} / 2 } e^{\gamma \lambda  \mathcal{V} } e^{\gamma \lambda \mathcal{T} / 2 } \right) \left( e^{(1- 2\gamma) \lambda \mathcal{T} / 2 } e^{(1- 2\gamma) \lambda  \mathcal{V} } e^{(1- 2\gamma) \lambda \mathcal{T} / 2 } \right) \left( e^{\gamma \lambda \mathcal{T} / 2 } e^{\gamma \lambda  \mathcal{V} } e^{\gamma \lambda \mathcal{T} / 2 } \right)+ O(\lambda^4) \\
	&= e^{\gamma \lambda \mathcal{T} / 2 } e^{\gamma \lambda  \mathcal{V} } e^{(1-\gamma) \lambda \mathcal{T} / 2 } e^{(1- 2\gamma) \lambda  \mathcal{V} } e^{(1-\gamma) \lambda \mathcal{T} / 2 }  e^{\gamma \lambda  \mathcal{V} } e^{\gamma \lambda \mathcal{T} / 2 } + O(\lambda^4) \\
	\gamma &= \frac{1}{2 - \sqrt[3]{2}}
\end{align*}
You can, in principle, get a splitting method for an arbitrary level of accuracy, but it will involve more computational time.\footnote{Referenced http://www.pci.uni-heidelberg.de/tc/usr/andreasm/academic/handouthtml/node13.html for part of this derivation}

\section{Trapezoidal Integration for Perturbation Theory Calculations with Smaller Time Discretization}

The integral one performs to do one ``interaction'' with a perturbative Hamiltonian $H'(t)$ with a time-independent Hamiltonian $H_0$ is:
\begin{align*}
	\ket{\Psi'(t)} = -\frac{i}{\hbar} \int_{-\infty}^{t} e^{-\frac{i}{\hbar} H_0 (t - \tau)} H'(\tau) \ket{\Psi (\tau)} d\tau
\end{align*}
Where $\ket{\Psi (\tau)} $ is the underlying wavefunction being interacted with, $\tau$ represents the different times that the interaction can happen at and $e^{-\frac{i}{\hbar} H_0 (t - \tau)} = U_0 (t, \tau)$ is the non-perturbative time-evolution operator.  This integral  phenomenologically represents some field represented by $H'$ having being able to interact at many different points in time--all where the field is nonzero.  At each of these points in time (represented by $\tau$), one must interact the field with the underlying wavefunction and then propagate that wavefunction out to the time-point of interest.  Then, for every $\tau$ before $t$ there is a contribution to the interacted wavefunction at time $t$ that is averaged over by the integral.

Now it is not hard to imagine that a naive implementation of this integral would be numerically very expensive.  Indeed, if you turn my above paragraph into an algorithm directly, if the perturbation is ``on'' for $N$ time steps, you are storing $O(N^2)$ wavefunctions which gets hairy very quickly.

We can do better, though and we will: to start with, we will assume that our perturbation can be ``turned on'' at a time $t_0 > -\infty$ without affecting the calculation at all, the integral reduces to this.
\begin{align*}
	\ket{\Psi'(t)} = -\frac{i}{\hbar} \int_{t_0}^{t} U_0 (t, \tau) H'(\tau) \ket{\Psi (\tau)} d\tau
\end{align*}
now we shall discretize time: $t_i = t_0 + i \delta t$ and $\tau_j = t_0 + j \delta t$.  $t$ and $\tau$ have the same initial value because $\tau$ is never lower than $t$.  Later on we will exploit this to simplify the expression but for now, I shall keep $\tau$ separate to make it abundantly clear that it is the variable which represents the point at which the perturbation acts on the system.

We decide to perform the integral with a simple Riemann sum:
\begin{align*}
	\int_{t_0}^{t_f} f(t) dt =\delta t \sum_{n} f(t_n) + O(\delta t^2)
\end{align*}
which gives us:
\begin{align*}
	\ket{\Psi'(t_i)} = -\frac{i}{\hbar} \delta t  \sum_{j=0}^{j=i}   U_0 (t_i, \tau_j) H'(\tau_j) \ket{\Psi (\tau_j)}
\end{align*}
now at this point, we decide to try mathematical induction for the heck of it and we calculate
\begin{align*}
	\ket{\Psi'(t_{i+1})} &= -\frac{i}{\hbar} \delta t  \sum_{j=0}^{j=i+1}   U_0 (t_{i+1}, \tau_j) H'(\tau_j) \ket{\Psi (\tau_j)} \\
									&= -\frac{i}{\hbar} \delta t \left[ \sum_{j=0}^{j=i}   U_0 (\delta t) U_0 (t_{i}, \tau_j) H'(\tau_j) \ket{\Psi (\tau_j)}  + U_0 (t_{i+1}, \tau_{i+1}) H'(\tau_{i+1}) \ket{\Psi (\tau_{\tau_{i+1})}} \right]
\end{align*}
which, since $t_n = \tau_n $ we can say
\begin{align*}
	\ket{\Psi'(t_{i+1})} = U_0 (\delta t)\ket{\Psi'(t_i)} -\frac{i}{\hbar} \delta t H'(\tau_{i+1}) \ket{\Psi (\tau_{\tau_{i+1})}}
\end{align*}
which has a really interesting interpretation.  At each time step, we merely take the previous time step's perturbation and propagate it forward one step in time.  Then, we take the perturbation and interact it with the current step of the wavefunction and add it to the propagated step and that is the current step's perturbed wavefunction.  This is a phenomenal savings in space and time of calculation over an naive implementation of the integral.

This is not without it's problems, however.  The error in this integration is of the order $\delta t^2$ whereas the error for most split-operator propagation methods (if one is being smart) is of the order $\delta t^3$.  This is entirely a failing of the Riemann sum algorithm.  Remembering back to High school calculus, though, one might recall another method called trapezoidal rule for integration.
\begin{align*}
	\int_{t_0}^{t_f} f(t) dt =\frac{\delta t}{2} \left[ f(t_0) + f(f_f) + 2  \sum_{n=1}^{n=f-1} f(t_n) \right] + O(\delta t^3)
\end{align*}
which we make use of to get an even lower error
\begin{align*}
	\ket{\Psi'(t_{i+1})} = U_0 (\delta t)\ket{\Psi'(t_{i})} -\frac{i}{\hbar}  \frac{\delta t}{2} U_0 (\delta t) H'(\tau_{i}) \ket{\Psi (\tau_{i})} -\frac{i}{\hbar} \frac{\delta t}{2} H'(\tau_{i+1}) \ket{\Psi (\tau_{i+1})}
\end{align*}

\section{Calculation Method For Heating Calculations}
To look at molecular heating, we would care most about the average vibrational quantum number
\begin{align*}
	\bar{n}(t) &= \sum_{n} n \left| \bra{g}\braket{n_{\gamma} | \Psi(t)} \right|^2
\end{align*}
But then what does the time-dependent wavefunction look like?  We're interested in the exact picture.  The interaction Hamiltonian is
\begin{align*}
	\hat{H}'(t) &= E(t)  \left(\ket{g}\bra{e} + \ket{e}\bra{g}\right)\mu(x)
\end{align*}
and the time-independent Hamiltonian is as above:
\begin{align}
	H_0 &=  \sum_n \hbar \omega_{\gamma}  \left(n + \frac{1}{2} \right)  \ket{n_{\gamma}}\ket{g}\bra{g} \bra{n_{\gamma}} \\
   &+ \sum_m \left(  \hbar \omega_{\epsilon}  \left(m + \frac{1}{2} \right) + \omega_e \right)  \ket{m_{\epsilon}} \ket{e}\bra{e} \bra{m_{\epsilon}}
\end{align}
If we turn the electronic degrees of freedom, putting it into Matrix form, we then have:
\begin{align*}
	\hat{H}(t) &=
	\begin{bmatrix}
	H_{\gamma}(x) & E(t)\mu(x) \\
	E(t)\mu(x) & H_{\epsilon}(x)
	\end{bmatrix}
\end{align*}
no that won't work since the coefficients of the Pauli matrices won't commute...

Let's try a blast form undergrad and calculate the time-dependent coefficients!  We assume the following form of the time-varying wavefunction:

\begin{align*}
	\ket{\Psi(t)} = \sum_{a} G_a(t) \ket{a_{\gamma}} \ket{g}  + \sum_{b} E_b(t) \ket{b_{\epsilon}} \ket{e}
\end{align*}
where we know the initial conditions are $G_0(0) = 1, G_{a\neq0}(0) = 0, E_b(0) = 0$.
We now take the full time-dependent Schrodinger equation:
\begin{align*}
	\frac{d}{dt}\ket{\Psi(t)} = -\frac{i}{\hbar}\hat{H}(t)\ket{\Psi(t)}
\end{align*}
starting with the left and simplest side:
\begin{align*}
	\frac{d}{dt}\ket{\Psi(t)} = \sum_{a} \frac{d G_a(t)}{dt} \ket{a_{\gamma}} \ket{g}  + \sum_{b} \frac{d E_b(t)}{dt} \ket{b_{\epsilon}} \ket{e}
\end{align*}
Then the right side can be split in twain:
\begin{align*}
	-\frac{i}{\hbar}\left( \hat{H}_0 + \hat{H}'(t)  \right) \ket{\Psi(t)}
\end{align*}
the easiest part of that being:
\begin{align*}
	\hat{H}_0  \ket{\Psi(t)} &= \sum_{a} G_a(t) \left[E_g + E_{\gamma}\left(a + \frac{1}{2}\right) \right]\ket{a_{\gamma}} \ket{g}  \\
	&+ \sum_{b} E_b(t) \left[E_e + E_{\epsilon}\left(b + \frac{1}{2}\right) \right] \ket{b_{\epsilon}} \ket{e}\\
	&= \sum_{a} G_a(t) \Omega_{(a)} \ket{a_{\gamma}} \ket{g}  \\
	&+ \sum_{b} E_b(t) \Omega^{(b)} \ket{b_{\epsilon}} \ket{e}
\end{align*}
and now for the interaction term:
\begin{align*}
	\frac{\hat{H}'(t)}{E(t)}  \ket{\Psi(t)} &= \sum_{a} G_a(t) \mu(x)\ket{a_{\gamma}} \ket{e}  + \sum_{b} E_b(t) \mu(x)\ket{b_{\epsilon}} \ket{g}
\end{align*}
we may find it useful to diagonalize the vibrational eigenstates into the same manifold as the electronic state using the identities: $\sum_{q}
\ket{q_{\gamma}}\bra{q_{\gamma}}$ and $\sum_{j}
\ket{j_{\epsilon}}\bra{j_{\epsilon}}$.  We also define $\mu_{a}^{b} = \bra{a_{\gamma}} \mu(x) \ket{b_{\epsilon}}$:
\begin{align*}
	\frac{\hat{H}'(t)}{E(t)}  \ket{\Psi(t)} &= \sum_{a, q} G_a(t) \mu_{a}^{q}\ket{q_{\epsilon}} \ket{e}  + \sum_{b,r} E_b(t) \mu_{r}^{b}\ket{r_{\gamma}} \ket{g}
\end{align*}
Now we put everything together:
\begin{align*}
	\left(\frac{d}{dt} +\frac{i}{\hbar}\hat{H}_0 \right)\ket{\Psi(t)}  = -\frac{i}{\hbar}\hat{H}''(t)\ket{\Psi(t)} \\
	\sum_{a} \left(\frac{d G_a(t)}{dt} + \frac{i}{\hbar}\Omega_{(a)}\right)\ket{a_{\gamma}} \ket{g}  + \sum_{b} \left(\frac{d E_b(t)}{dt} + \frac{i}{\hbar} \Omega^{(b)} \right)\ket{b_{\epsilon}} \ket{e}\\
	=-E(t)\frac{i}{\hbar} \left[\sum_{a, q} G_a(t) \mu_{a}^{q}\ket{q_{\epsilon}} \ket{e}  + \sum_{b,r} E_b(t) \mu_{r}^{b}\ket{r_{\gamma}} \ket{g} \right]
\end{align*}
Now we can pick out specific terms by ket-ing in with $\ket{g}\ket{a_{\gamma}}$ and $\ket{b}\ket{b_{\epsilon}}$.
\begin{align*}
	\left(\frac{d G_a(t)}{dt} + \frac{i}{\hbar}\Omega_{(a)}\right) &=-E(t)\frac{i}{\hbar}  \sum_{b} E_b(t) \mu_{a}^{b}
\end{align*}
and then:
\begin{align*}
	\left(\frac{d E_b(t)}{dt} + \frac{i}{\hbar} \Omega^{(b)} \right) =-E(t)\frac{i}{\hbar} \sum_{a} G_a(t) \mu_{a}^{b}
\end{align*}

This can be re-cast as a matrix problem,
\begin{align*}
	\frac{d}{dt}\begin{bmatrix}
		G_a(t) \\
		E_b(t)
	\end{bmatrix}
	= -\frac{i}{\hbar}
	\begin{bmatrix}
		\Omega_{(a)} & E(t) \mu_{a}^{b} \\
		E(t) \mu_{a}^{b} & \Omega^{b}
	\end{bmatrix}
	\cdot
	\begin{bmatrix}
		G_a(t) \\
		E_b(t)
	\end{bmatrix}
\end{align*}
Then this is put into a numeric first order differential equation solver.  At every point I make sure there is not too much amplitude in the highest vibrational state and then re-start the calculation if so, ensuring no nonphysical results from truncation of the Hilbert Space.

\section{Notorious RWA: the Rotating Wave Approximation}

There's another trick we can use to save a lot of computational time called the rotating wave approximation.  Let's take a look at some laser excitation operator:
\begin{align*}
	\hat{H}_{+}'(t) = E_0 e^{-i \omega_c \tau} e^{\frac{\tau^2 }{2 \sigma^2}}  \ket{e}\bra{g}
\end{align*}
if we assume the ground state has zero energy and the excited state has energy/frequency (because $\hbar=1$ here) $\Omega$ then we expect the zero and first order perturbation to look like:
\begin{align*}
	\ket{\psi_0 (t)} &= \ket{g} \\
	\ket{\psi_{+} (t)} &= -\frac{i}{\hbar} \int_{-\infty}^{t} U(t, \tau) \hat{H}_{+}'(\tau) \ket{g} d \tau \\
	&= -\frac{iE_0}{\hbar} \int_{-\infty}^{t} U(t, \tau)e^{-i \omega_c \tau} e^{\frac{\tau^2  }{2 \sigma^2}}  \ket{e} d \tau \\
	&= -\frac{iE_0}{\hbar} \int_{-\infty}^{t} e^{ -i\Omega (t - \tau)}e^{-i \omega_c \tau} e^{\frac{\tau^2 }{2 \sigma^2}}  \ket{e} d \tau \\
	&= -\frac{iE_0}{\hbar} e^{ -i\Omega t} \ket{e} \int_{-\infty}^{t} e^{-i ( \omega_c - \Omega) \tau} e^{\frac{\tau^2 }{2 \sigma^2}} d \tau
\end{align*}
so we see that after the excitation perturbation only depends on $\omega_c - \Omega$ or the difference between the central frequency of the pulse and the laser.  Which means that we can wrap those two things together and say that the energy of the excited state is zero and that the laser oscillates at a frequency of $\omega_c - \Omega$ instead.
\begin{align*}
	\ket{\psi_0 (t)} &= \ket{g} \\
	\ket{\psi_{+} (t)} &= -\frac{iE_0}{\hbar}\ket{e} \int_{-\infty}^{t} e^{-i \delta \omega \tau} e^{\frac{\tau^2 }{2 \sigma^2}} d \tau
\end{align*}
What does that give us computationally?  It means, primarily, that we can use a smaller time step and have the same accuracy, which will greatly speed up the calculation.  One must be careful, though.  It only works for diagonal Hamiltonians.  Once one has a diagonal (or block diagonal) Hamiltonian, one can use this trick to consider the possible transitions between the diagonal states.


% !TeX root = ../smc-report.tex
% !TeX encoding = UTF-8
% !TeX spellcheck = en_GB

\section*{PRISM Tutorial Part 3: Dynamic power management}

  \subsection*{Dynamic power management}

    In this section will be described the modelisation through PRISM \cite{KNP11} of a \textit{DPM} (\textit{Dynamic Power Management}) system, following the PRISM tutorial found at \cite{prism-tutorial3}. DPMs are used to apply different power usages to some computing device, according to a predefined strategy that takes into account the current state of the device. This kind of systems have been studied largely in literature, for example in \cite{qiu2001stochastic} where a DPM for a Fujitsu disk drive has been studied.

    A generic DPM system is made of three distinct components:

    \begin{itemize}
      \item \textit{Service Queue} (\textit{SQ}): holds the requests that the Service Provider will have to serve, in an ordered fashion, and can have finite queue capacity;
      \item \textit{Service Provider} (\textit{SP}): serves, one at a time, the requests stored in the Service Queue, serving each time the request at the head of the queue;
      \item \textit{Power Manager} (\textit{PM}): can change the power state of the Service Provider according to certain policies.
    \end{itemize}

    The \textit{SP} could be anything that is a computing device that serves requests, such as a disk drive as in \cite{qiu2001stochastic}, but also a CPU or a Web Server.

    At any given time, the \textit{SP} is in one of three possible power states, each of which:

    \begin{itemize}
      \item \textit{sleep}: the \textit{SP} is in a low-power consumption mode and is unable to serve any request unless explicitly awaken by the \textit{PM};
      \item \textit{idle}: the \textit{SP} is awake but currently not serving any request, so any newly arriving request will be served immediately by the \textit{SP};
      \item \textit{busy}: the \textit{SP} is currently serving a request and will be available to serve the next in queue as soon as it's finished.
    \end{itemize}

    Ideally, when in the \textit{sleep} state the \textit{SP} will be requiring little to none power, when in the \textit{idle} state it will require more, as it is awake and ready to serve requests, while when \textit{busy} it will require even more, as the \textit{SP} in that case is actively working on a request. The \textit{PM} is charged with employing a power consumption strategy by switching the \textit{SP}'s power state, in order to maximise the availability of the service while minimising the overall power consumption.

    A first PRISM model for a DPM based on \cite{qiu2001stochastic} is proposed in Code \ref{lst:power}, as seen in \cite{prism-tutorial3}.

    \begin{center}
      \lstinputlisting[language=prism, caption={PRISM code for the model of a DPM based on \cite{qiu2001stochastic}, with only the \textit{SQ} and the \textit{SP} components. Source \cite{prism-tutorial3}.}, label={lst:power}]{code/power.sm}
    \end{center}

    In this first model version shown in Code \ref{lst:power}, only the two modules, for the \textit{SQ} and the \textit{SP} components, are introduced. This is an already working strategy, even without the \textit{PM} component, which would only add a smarter policy for the \textit{SP}'s power management.

    It is worth noticing that the model shown in Code \ref{lst:power} is described as a \textit{Continuous Time Markov Chain} (\textit{CTMC}), which allows the use of rates when defining the firing of transitions instead of simple probabilities.

    \question{Read the section on \href{http://www.prismmodelchecker.org/manual/ThePRISMLanguage/Synchronisation}{synchronisation} in the manual. Then, have a look at the definition of the \prism{SQ} and \prism{SP} modules, and try to understand what they describe.}
    \answer{
      The \prism{SQ} and \prism{SP} modules implement, respectively, the \textit{SQ} and \textit{SP} components of the DPM system. Both modules synchronise on three different kinds of actions: \prism{request}, indicating the arrival of a new request, \prism{serve}, indicating that a request (except the last) has been served, and \prism{serve_last}, indicating that the last request in the queue has been served.

      The \prism{SQ} module keeps a variable, \prism{q}, that represents the current number of request in the queue, with maximum capacity given by the constant \prism{q_max}. When, with the predefined arrival rate \prism{rate_arrive}, a new request arrives (line 26) the queue is updated accordingly, eventually discarding requests in excess in case of a full queue. When a request is served by the \prism{SP} module, whether it was the last (line 30) or not (line 28), the queue is decreased accordingly. The distinction for these two cases might seem useless on the \prism{SQ} side, but it will prove useful for the \prism{SP} module.

      The \prism{SP} module only keeps a variable, \prism{sp}, indicating the current power state (0 meaning \textit{sleep}, 1 \textit{idle} and 2 \textit{busy}), which starts in the \textit{idle} state. When a new request arrives, the \textit{SP} is switched to the \textit{busy} state in case it was \textit{idle} (line 51), indicating that it started working on that request right away, while if the power state is either \textit{sleep} or \textit{busy} then it's kept the same (line 54). When a request that was not the last in the queue is served (line 52), with service rate defined by the variable \prism{rate_serve}, the \textit{SP} is kept in the busy state, meaning that it starts working on the next request in line. Otherwise, if the last request is served (line 58), employing the same service rate, the \textit{SP} is switched back to the \textit{idle} state.

      It is worth noticing that, since by definition only the \textit{PM} component can decide when the \textit{SP} has to wake, in this first model, if the \textit{SP} starts in the \textit{sleep} state, it would have no way of awaking.
    }

    \question{Download the model file \bash{power.sm} from above and load it into PRISM.\\}
    \trivialanswer{
      The model \bash{power.sm} loaded into PRISM is shown in Figure \ref{fig:loaded_model}.
    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.8]{loaded_model.png}
    		\end{center}
    		\caption{Model \bash{power.sm} loaded into PRISM.}
    		\label{fig:loaded_model}
    	\end{figure}
    }

    \question{Use the PRISM simulator to generate some random paths through the model. Notice how, for a CTMC model like this, the elapsed time as the path progresses is displayed in the table. You will probably find that the size of the queue (\prism{q}) never gets above 1. Why is this? Generate a path by hand where the queue reaches its maximum size (currently \prism{q_max}=20). What happens when more requests arrive while the queue is full?}
    \answer{
      By repeatedly selecting the ``Simulate'' button in the PRISM simulator with 1 step it can effectively be seen that the queue size almost never exceeds 1. This because of how arrival and service rates are defined: while the arrival rate of new requests is $1.3\overline{8}$, meaning that on average a new request arrives every $0.72$ seconds, the service rate is $125$, meaning that on average a request is served in $0.008$ seconds. This means that, when a request is in the queue, the service time of that request is, on average, $90$ times faster than the arrival of a new request. So, although possible, it's just highly unlikely that, with rates so defined, a new request arrives before a service is finished.

      If instead the action \prism{request} is repeatedly selected in the PRISM simulator in order to force the arrival of new requests before the service, a full queue can be reached. In this case, as expected, more request arrivals end up in the refusal of these new requests, simply no including them in the queue, which remains at it's full capacity (\prism{q_max}=20 in this case). It is worth noticing that, in case a new request arrives before a service is completed, even though the \prism{serve} action remains enabled, it's probability distribution remains the same, since these are all exponentially distributed transitions and thus memoryless.
    }

    \question{What is the size of the state space of this model? (i.e from the initial state, how many possible different states can be reached?) Go back to the ``Model'' tab of the GUI, select menu option ``Model | Build model'' and then look at the statistics displayed in the bottom left corner to check your answer.}
    \answer{
      By selecting the ``Build model'' feature, it can be seen that the state space of this model is made of a total of 21 states. Intuitively, these are made of a state where the \textit{SP} is \textit{idle} and the queue empty and 20 states where the \textit{SP} is \textit{busy} and the queue has a different number of pending requests (from 1 to 20).
    }

  \dashedrule

  \subsection*{Adding the power management control}

    Now we add to the PRISM model shown in Code \ref{lst:power} and additional module, \prism{PM}, responsible of implementing the Power Manager. The \textit{PM} is the component of a DPM that is charged with waking the \textit{SP} from sleep or putting it back to sleep according to a specific policy that might be dependent of several factors, such as the current state of the \textit{SP} or of the \textit{SQ}.

    Code \ref{lst:power_policy1} shows the updated model with the added \prism{PM} module, which employs a fairly naive policy.

    \begin{center}
      \lstinputlisting[language=prism, caption={PRISM code for the model of a DPM based on \cite{qiu2001stochastic}, with the \textit{SQ}, \textit{SP} and \textit{PM} components. Source \cite{prism-tutorial3}.}, label={lst:power_policy1}]{code/power_policy1.sm}
    \end{center}

    \question{Look at the code we have added to the \prism{SP} module and at the new \prism{PM} module. Make sure you understand how they work.}
    \answer{
      The \prism{PM} module has the function of wake the \textit{SP} or to put it back to sleep according, in this case, to the current size of the queue.

      In particular, when in the queue there are at least \prism{q_trigger} elements the modules \prism{PM} and  \prism{SP} can synchronise on the action \prism{sleep2idle} (lines 56, 57 and 87), effectively waking up the \textit{SP} in case it was still asleep, as the action name suggests. If, when the awakening is triggered, the queue \prism{q} is empty then the \textit{SP} is switched to the \textit{idle} state, otherwise it's switched directly to the \textit{busy} state, i.e. working on the request on top of the queue.

      Whenever, instead, the queue becomes empty, the \prism{PM} and \prism{SP} modules can synchronize on the \prism{idle2sleep} action (lines 59 and 90), effectively waking up the \textit{SP}.

      It is worth noticing how the operations of waking the \textit{SP} up and putting it back to sleep are not immediate but instead, when the corresponding conditions are met, are characterised by rates (\prism{rate_s2i} and \prism{rate_i2s}, respectively). Also, in this second version of the model, the \prism{SP}'s power state is now initialised as 0 (i.e. \textit{sleep} state), since now, thanks to the \prism{PM} module, initialising the \textit{SP} in the \textit{sleep} state wouldn't end up in a deadlock any more.
    }

    \question{Now use the simulator to generate a trace through this new model. When you create a new path, you have to specify a value for the constant \prism{q_trigger}, because it is left undefined in the model. Try a value of 5 for now. Does this new model behave as you expect?}
    \answer{
      After a few simulation steps, it can be seen that the model actually behaves as expected. In particular, between 0 and 4 requests the \textit{SP} has no other option than staying in the \textit{sleep} state and, thus, the model has just one available transition at each step, that is the arrival of new requests. When the queue reaches 5 requests, the waking condition is met, but sometimes the awakening is not immediate and some additional requests (usually 1 or 2) manage to arrive before the \textit{SP} is properly awaken: this is due the fact that the awaking rate (\prism{rate_s2i}) is slightly smaller than the request arrival rate (\prism{request}), meaning that on average the arrival of a new request is slightly faster than the awakening of \textit{SP}.

      Similarly, once the queue has been emptied, the \textit{SP} can be switched back to \textit{sleep} right away or it could happen that a request manages to arrive before that happens, forcing the \textit{SP} to serve it first by switching to the \textit{busy} state.
    }

  \dashedrule

  \subsection*{Analysing the model}

    We'll analyse now various aspects of the model, exploiting the properties and analyses features that PRISM offers.

    \question{
      Go to the "Properties" tab of the GUI, create a new constant called T, of type double and with no defined value. Then add the following property:

      \lstinputlisting[language=prism, numbers=none]{code/q_max_P.pctl}
    }
    \trivialanswer{
      The property regarding the full queue added through the PRISM interface is shown in Figure \ref{fig:q_max_property}.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.8]{q_max_property.png}
    		\end{center}
    		\caption{Full queue property added in PRISM.}
    		\label{fig:q_max_property}
    	\end{figure}
    }

    \question{Now, create an \href{http://www.prismmodelchecker.org/manual/RunningPRISM/Experiments}{experiment} based on this property, plotting a graph of its result for \prism{q_trigger} equal to 5 and \prism{T} from 0 up to 20, i.e. for the first 20 seconds of the system.}
    \answer{
      The resulting experiment plot, with steps of $0.5$ seconds, is shown in Figure \ref{fig:q_max_plot_0-20}.
    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{q_max_plot_0-20.eps}
    		\end{center}
    		\caption{Experiment plot of the full queue between time 0 and 20.}
    		\label{fig:q_max_plot_0-20}
    	\end{figure}
    }

    \question{It seems that the transient probability of the queue being full stabilises after a short while. Using another experiment, plot values for the same property on the same graph, this time for \prism{T} from 20 up to 40, and see if the probability remains the same. To confirm this, now create a property to check the long-run probability of the queue being full, using the \href{http://www.prismmodelchecker.org/manual/PropertySpecification/TheSOperator}{S operator}. Right click the new property and select ``Verify''. You will need to give a value for \prism{T} but this is not used so you can enter anything. It turns out that the default iterative method in PRISM (Jacobi) for solving this kind of property does not converge in this case. Switch to the Gauss-Seidel method from the ``Options'' dialog and try again. Check that your result matches the graph.}
    \answer{
      From the extended plot shown in Figure \ref{fig:q_max_plot_0-40} it seems that the probability of reaching a full queue indeed stabilises.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{q_max_plot_0-40.eps}
    		\end{center}
    		\caption{Experiment plot of the full queue between time 0 and 40.}
    		\label{fig:q_max_plot_0-40}
    	\end{figure}

    	In order to check the steady-state probability of the queue being full in a more direct fashion, the following property, that exploits the S operator, can be used:

      \lstinputlisting[language=prism, numbers=none]{code/q_max_S.pctl}

      By trying to evaluate the property using the default iterative method (Jacobi) the evaluation indeed does not converge and PRISM prompts an error. Instead, using the Gauss-Seidel iterative method, the computed steady-state probability of the queue being full results being $0.0010287642322871614$. Watching at the plot in Figure \ref{fig:q_max_plot_0-40} it looks like the probability of a full queue stabilises slightly above $0.0010$, which matches with the obtained result.
    }

    \question{Read the section on \href{http://www.prismmodelchecker.org/manual/ThePRISMLanguage/CostsAndRewards}{costs and rewards} in the manual. Then, look at the rewards that have already been defined in this model.}
    \answer{
      The only reward that has been defined in the model in Code \ref{lst:power_policy1} is \prism{queue_size} which, at any given time, is equivalent to the current number of requests present in the queue.
    }

    \question{
      Add these two properties which can be used to compute the transient and long-run expected queue size, respectively:

      \lstinputlisting[language=prism, numbers=none]{code/queue_size.pctl}
    }
    \trivialanswer{
      The properties regarding the queue size added through the PRISM interface are shown in Figure \ref{fig:queue_size_properties}.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.8]{queue_size_properties.png}
    		\end{center}
    		\caption{Queue size properties added in PRISM.}
    		\label{fig:queue_size_properties}
    	\end{figure}
    }

    \question{Plot the expected queue size at time \prism{T}, for the first 20 seconds of the model. As above, also compute the long-run value and check that it matches the results on the graph.}
    \answer{
      The experiment plot is shown in Figure \ref{fig:queue_size_plot}.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{queue_size_plot.eps}
    		\end{center}
    		\caption{Experiment plot of the queue size between time 0 and 20.}
    		\label{fig:queue_size_plot}
    	\end{figure}

    	Evaluating instead the steady-state property, it can be seen that the number of requests in the queue at steady-state is $3.2038846166301242$, which seems to match to the transient results shown in the plot in Figure \ref{fig:queue_size_plot}.
    }

    \question{Now create some experiments to analyse the transient and/or long-run expected queue size for a range of different values of the constant \prism{q_trigger}. How does the performance of the system vary as this changes? What is the ``best'' value of \prism{q_trigger}?}
    \answer{
      Transient analysis results between time 0 and 40 and for \prism{q_trigger} between 0 and 21 are shown in Figure \ref{fig:queue_size-q_trigger_plot_I}. The last value, \prism{q_trigger}=21, has been included to show what happens in a scenario where the \textit{SP} never wakes.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{queue_size-q_trigger_plot_I.eps}
    		\end{center}
    		\caption{Experiment plot of the queue size between time 0 and 40 and for \prism{q\_trigger} between 0 and 21.}
    		\label{fig:queue_size-q_trigger_plot_I}
    	\end{figure}

      As expected, the average queue size is higher for higher values of \prism{q_trigger}. After some time this value tends to stabilise, although for higher values of \prism{q_trigger} it seems as it needs more time in order to reach the steady-state. For \prism{q_trigger}=21, as expected, the queue fills completely and never gets the change to get emptied. According to the plot in Figure \ref{fig:queue_size-q_trigger_plot_I} we could say that the ``best'' values for \prism{q_trigger} are the lowest (from 0 to 5), since for those values the average queue size stabilises earlier, thus producing a more predictable behaviour.

      In Figure \ref{fig:queue_size-q_trigger_plot_S} is instead shown the average queue size at steady-state, for the same range of values for \prism{q_trigger}.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{queue_size-q_trigger_plot_S.eps}
    		\end{center}
    		\caption{Experiment plot of the queue size at steady-state for \prism{q\_trigger} between 0 and 21.}
    		\label{fig:queue_size-q_trigger_plot_S}
    	\end{figure}

      As expected, the average queue size rises as \prism{q_trigger} grows and it's exactly 20 for \prism{q_trigger}$\geq 21$. But is can also be observed that the growth is not exactly linear: for example for \prism{q_trigger}=1 the average queue size is $1.734$, while for \prism{q_trigger}=3 it's $2.383$.

      Ideally, we would like to have the queue as empty as possible at any time, because that would mean that requests are handled rapidly and there is more room for future requests, while at the same time consuming as few resources as possible, for example by keeping the \textit{SP} in the \textit{sleep} state for longer. In Figure \ref{fig:queue_size_coefficient_plot} we are showing then the \textit{queue size coefficient}, evaluated as \prism{q/q_trigger}, in order to find the best trade-off between these two factors. In particular, the coefficient is added to the PRISM model by including the following reward structure:

      \lstinputlisting[language=prism, numbers=none]{code/queue_size_coefficient.sm}

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{queue_size_coefficient_plot.eps}
    		\end{center}
    		\caption{Experiment plot of the queue size coefficient at steady-state for \prism{q\_trigger} between 0 and 21.}
    		\label{fig:queue_size_coefficient_plot}
    	\end{figure}

      The results in Figure \ref{fig:queue_size_coefficient_plot} shows that, for \prism{q_trigger}=1 and 2, the coefficient is above 1, meaning that the average queue size is usually higher than the value of \prism{q_trigger}. For \prism{q_trigger}$\geq 3$ instead, the coefficient is lower than zero and decreasing, except for \prism{q_trigger}=21, which becomes higher again. According to this experiment then, we are able to say that the ``best'' value for \prism{q_trigger} is $20$, since in this case the queue size coefficient is at it's lowest (looking at the plot in Figure \ref{fig:queue_size-q_trigger_plot_S} we can see that for \prism{q_trigger}=20 the average queue size is $10.126$).
    }

    \question{Add a second \href{http://www.prismmodelchecker.org/manual/ThePRISMLanguage/CostsAndRewards}{reward structure} to the model called ``lost'', which assigns 1 to every transition of the model labelled with action \prism{request} from a state where the queue is full.}
    \answer{
      The reward ``lost'' is simply included in the PRISM model by adding at the end the following lines:

      \lstinputlisting[language=prism, numbers=none]{code/lost.sm}
    }

    \question{Now create a new property to check the expected \textit{cumulated} reward up until time \prism{T}. Don't forget to specify which reward structure you want in the property. (You might want to look at \href{http://www.prismmodelchecker.org/manual/PropertySpecification/Reward-basedProperties}{this section} of the manual.) How does this measure vary for different values of \prism{q_trigger}?}
    \answer{
      In Figure \ref{fig:lost_plot} are shown the results for the cumulative lost requests between time 0 and 40 and for values of \prism{q_trigger} between 0 and 20. The experiment is conducted using the following property:

      \lstinputlisting[language=prism, numbers=none]{code/lost_cumulative.pctl}

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{lost_plot.eps}
    		\end{center}
    		\caption{Experiment plot of the cumulative lost requests between time 0 and 40 and for \prism{q\_trigger} between 0 and 20.}
    		\label{fig:lost_plot}
    	\end{figure}

      As expected, the cumulative lost requests are more as time goes on, but also for higher values of \prism{q_trigger} this values grows at higher rates. This is intuitive since when the \textit{SP} only wakes up after more requests have arrived in the queue, the chances of reaching the queue saturation are higher.
    }

    Let's imagine now to introduce a measure of the actual power consumption. In particular, energy consumption rates are $0.13$, $0.95$ and $2.15$, for the power states \textit{sleep}, \textit{idle} and \textit{busy}, respectively. On top of that, switching from \textit{sleep} to \textit{idle} costs $7.0$ energy units, while from \textit{idle} to \textit{sleep} costs $0.067$ energy units.

    \question{Add a third reward structure to the model representing the power consumption. Use a cumulative reward property to investigate the energy consumption over time of the system.}
    \answer{
      First of all, in order to add the desired reward structure, the following rates have to be included in the model:

      \lstinputlisting[language=prism, numbers=none]{code/consumption_rates.sm}

      Then, inside the \prism{SP} module, the following transitions have to be included:

      \lstinputlisting[language=prism, numbers=none]{code/consumption_transitions.sm}

      Finally, the ``consumption'' reward structure can be defined as follows:

      \lstinputlisting[language=prism, numbers=none]{code/consumption_reward.sm}

      Experiment results are shown in Figure \ref{fig:consumption_policy1_plot}.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{consumption_policy1_plot.eps}
    		\end{center}
    		\caption{Experiment plot of the cumulative energy consumption between time 0 and 40 and for \prism{q\_trigger} between 0 and 20.}
    		\label{fig:consumption_policy1_plot}
    	\end{figure}

      As expected, the cumulative energy consumption grows as time goes on, but it can also be observed that the energy consumption is lower for higher values of \prism{q_trigger}, which is obvious considering that in these scenarios the \textit{SP} spends, on average, more time in the \textit{sleep} state, consuming less energy.
    }

  \dashedrule

  \subsection*{Extensions}

    \question{
      Replace the PM module in the existing model with the following:\\

      \lstinputlisting[language=prism, numbers=none]{code/PM_stochastic_policy.sm}
    }
    \trivialanswer{
      The model with stochastic policy loaded into PRISM is shown in Figure \ref{fig:loaded_model_stochastic_policy}.
    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.35]{loaded_model_stochastic_policy.png}
    		\end{center}
    		\caption{Model with stochastic policy loaded into PRISM.}
    		\label{fig:loaded_model_stochastic_policy}
    	\end{figure}
    }

    \question{How well does this power management system perform? What effect does the value of the probability \prism{p_sleep} have?}
    \answer{
      The results of the experiments concerning the cumulative energy consumption with the stochastic policy are shown in Figure \ref{fig:consumption_stochastic_plot}.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{consumption_stochastic_plot.eps}
    		\end{center}
    		\caption{Experiment plot of the cumulative energy consumption employing the stochastic policy, between time 0 and 40 and for \prism{p\_sleep} between 0 and 1, with step $0.1$.}
    		\label{fig:consumption_stochastic_plot}
    	\end{figure}

      Again, the energy consumption grows as the time goes on, but also it tends to be lower for higher values of \prism{p_sleep}, yielding top performance when \prism{p_sleep}=1, i.e. when the \textit{SP} always gets put to sleep after the queue has been emptied. On top of that, we can notice that while for the previous simpler policy the energy consumption, after 40 seconds, could reach, on average, almost 140 units (see Figure \ref{fig:consumption_policy1_plot}), with this new policy the maximum at \prism{T}=40 is $32.392$, for \prism{p_sleep}=0, making this second policy way more performing than the previous one.
    }

    \question{Can you think of any ways of improving these power management strategies? Implement them in the PRISM model and see how well they perform.}
    \answer{
      A possible way of minimising even further the energy consumption is to reverse the idea behind the stochastic policy introduced previously. In particular, we now want to always put the \textit{SP} to sleep when the queue has been emptied and wake it up only when the queue is full but only with a certain probability. This policy is implemented by the following code for the \prism{PM} module:

      \lstinputlisting[language=prism, numbers=none]{code/PM_custom_policy.sm}

      The results of the experimentation with this new policy are shown in Figure \ref{fig:consumption_custom_plot}.

    	\begin{figure}[h!]
    		\begin{center}
    			\includegraphics[scale=0.45]{consumption_custom_plot.eps}
    		\end{center}
    		\caption{Experiment plot of the cumulative energy consumption employing a new custom policy, between time 0 and 40 and for \prism{p\_sleep} between 0 and 1.}
    		\label{fig:consumption_custom_plot}
    	\end{figure}

      It can be observed that, by increasing the value of \prism{p_sleep}, i.e. the probability of remaining asleep even with a full queue, the cumulated energy consumption tends to be lower. This policy basically tries to minimise the number of times the \textit{SP} wakes up, being the wake up process a highly consuming action. Clearly, with this policy the number of lost requests is expected to be higher, but in a scenario where energy consumption is a central problem and some lost request is not such a big deal, this might be a good solution. In particular, the ``best'' case would be that with \prism{p_sleep}=1, but that would mean that the \textit{SP} never wakes up, which would be unrealistic.

      In order to further analyse the model, and devise smarter policies, both the energy consumption and the number of lost requests should be taken into account, in order to find the best trade-off.
    }

  \dashedrule