\section{Adding Mass to CLAuDE}
\subsection{The Momentum Equations}
The momentum equations are a set of equations that describe the flow of a fluid on the surface of a rotating body. For our model we will use the f-plane approximation. The equations corresponding
to the f-plane approximation are given in \autoref{eq:x momentum} and \autoref{eq:y momentum} \cite{momentumeqs}. Note that we are ignoring vertical moevement, as this does not have a significant
effect on the whole flow. All the symbols in \autoref{eq:x momentum} and \autoref{eq:y momentum} mean:

\begin{itemize}
    \item $u$: The east to west velocity ($ms^{-1}$).
    \item $t$: The time ($s$).
    \item $f$: The coriolis parameter as in \autoref{eq:coriolis}.
    \item $v$: The north to south velocity ($ms^{-1}$).
    \item $\rho$: The density of the atmosphere ($kgm^{-3}$).
    \item $p$: The atmospheric pressure ($Pa$).
    \item $x$: The local longitude coordinate ($m$).
    \item $y$: The local latitude coordinate ($m$).
\end{itemize}

If we then define a vector $\bar{u}$ as $(u, v, 0)$, we can rewrite both \autoref{eq:x momentum} as \autoref{eq:x momentum laplace}. Here $\nabla u$ is the gradient of
$u$ in both $x$ and $y$ directions. Then if we write out $\nabla u$ we get \autoref{eq:x momentum final}. Similarly, if we want to get $\delta v$ instead of $\delta u$ we rewrite
\autoref{eq:y momentum} to get \autoref{eq:y momentum laplace} and \autoref{eq:y momentum final}.

\begin{subequations}
    \begin{equation}
        \frac{Du}{Dt} - fv = -\frac{1}{\rho} \frac{\delta p}{\delta x}
        \label{eq:x momentum}
    \end{equation}
    \begin{equation}
        \frac{Dv}{Dt} - fu = -\frac{1}{\rho} \frac{\delta p}{\delta y}
        \label{eq:y momentum}
    \end{equation}
    \begin{equation}
        \frac{\delta u}{\delta t} + \bar{u} \cdot \nabla u - fv = -\frac{1}{\rho}\frac{\delta p}{\delta x}
        \label{eq:x momentum laplace}
    \end{equation}
    \begin{equation}
        \frac{\delta v}{\delta t} + \bar{u} \cdot \nabla v - fu = -\frac{1}{\rho}\frac{\delta p}{\delta y}
        \label{eq:y momentum laplace}
    \end{equation}
    \begin{equation}
        \frac{\delta u}{\delta t} + u\frac{\delta u}{\delta x} + v\frac{\delta u}{\delta y} - fv = -\frac{1}{\rho}\frac{\delta p}{\delta x}
        \label{eq:x momentum final}
    \end{equation}
    \begin{equation}
        \frac{\delta v}{\delta t} + u\frac{\delta v}{\delta x} + v\frac{\delta v}{\delta y} - fu = -\frac{1}{\rho}\frac{\delta p}{\delta y}
        \label{eq:y momentum final}
    \end{equation}
\end{subequations}

Now that we have the momentum equations sorted out, we need to define a method to do the gradient calculations for us. Therefore we define two functions \autoref{alg:gradient x} and
\autoref{alg:gradient y} that calculate the $x$ and $y$ gradients respectively.

\begin{algorithm}[hbt]
    \SetKwInOut{Input}{Input}
    \SetKwInOut{Output}{Output}
    \Input{Matrix (double array) $a$, first index $i$, second index $j$}
    \Output{Gradient in the $x$ direction}
    $grad \leftarrow \frac{a[i, (j + 1)\text{ mod } nlon] - a[i, (j - 1) \text{ mod } nlon]}{\delta x[i]}$ \;
    \Return{$grad$} \;
    \caption{Calculating the gradient in the $x$ direction}
    \label{alg:gradient x}
\end{algorithm}

\begin{algorithm}[hbt]
    \SetKwInOut{Input}{Input}
    \SetKwInOut{Output}{Output}
    \Input{Matrix (double array) $a$, first index $i$, second index $j$}
    \Output{Gradient in the $y$ direction}
    \eIf{$i == 0$ or $i == nlat - 1$}{
        $grad \leftarrow 0$ \;
    }{
        $grad \leftarrow \frac{a[i + 1, j] - a[i - 1 j]}{\delta y}$ \;
    }
    \Return $grad$ \;
    \caption{Calculating the gradient in the $y$ direction}
    \label{alg:gradient y}
\end{algorithm}

With the gradient functions defined, we can move on to the main code for the momentum equations. The main loop is shown in \autoref{alg:stream3}. Do note that this loop replaces the one
in \autoref{alg:stream2v2} as these calculate the same thing, but the new algorithm does it better.

\begin{algorithm}
    $S_{xu} \leftarrow \texttt{gradient\_x}(u, lan, lon)$ \;
    $S_{yu} \leftarrow \texttt{gradient\_y}(u, lan, lon)$ \;
    $S_{xv} \leftarrow \texttt{gradient\_x}(v, lan, lon)$ \;
    $S_{yv} \leftarrow \texttt{gradient\_y}(v, lan, lon)$ \;
    $S_{px} \leftarrow \texttt{gradient\_x}(p, lan, lon)$ \;
    $S_{py} \leftarrow \texttt{gradient\_x}(p, lan, lon)$ \;
    \While{\texttt{TRUE}}{
        \For{$lat \in [1, nlat - 1]$}{
            \For{$lon \in [0, nlon]$}{
                $u[lan, lon] \leftarrow u[lan, lon] + \delta t \frac{-u[lan, lon]S_{xu} - v[lan, lon]S_{yu} + f[lan]v[lan, lon] - S_{px}}{\rho}$ \;
                $v[lan, lon] \leftarrow v[lan, lon] + \delta t\frac{-u[lan, lon]S_{xv} - v[lan, lon]S_{yv} - f[lan]u[lan, lon] - S_{py}}{\rho}$ \;
            }
        }
    }
    \caption{Calculating the flow of the atmosphere (wind)}
    \label{alg:stream3}
\end{algorithm}

\subsection{Thermal Diffusion}
As of this time, what you notice if you run the model is that the winds only get stronger and stronger (and the model is hence blowing up). This is because there is no link yet between the
velocities of the atmosphere and the temperature. Currently, any air movement does not affect the temperature in the atmosphere of our model while it does in reality. So we need to change some
calculations to account for that. Thermal diffusion helps with spreading out the temperatures and tempering the winds a bit.

The diffusion equation, as written in \autoref{eq:diffusion}, describes how the temperature spreads out over time\cite{diffusion}. The symbols in the equation represent:

\begin{itemize}
    \item $u$: A vector consisting out of 4 elements: $x, y, z, t$. $x, y, z$ are the local coordinates and $t$ is time.
    \item $\alpha$: The thermal diffusivity constant.
    \item $\nabla^2$: The Laplace operator, more information in \autoref{sec:laplace}.
    \item $\bar{u}$: The time derivative of $u$, or in symbols $\frac{\delta u}{\delta t}$.
\end{itemize}

\begin{equation}
    \bar{u} = \alpha \nabla^2 u
    \label{eq:diffusion}
\end{equation}

Now to get this into code we need the following algorithms \autoref{alg:laplacian} and \autoref{alg:diffusion}. \autoref{alg:laplacian} implements the laplacian operator, whereas
\autoref{alg:diffusion} implements the diffusion calculations. $\Delta_x$ and $\Delta_y$ in \autoref{alg:laplacian} represents the calls to \autoref{alg:gradient x} and \autoref{alg:gradient y}
respectively. $\nabla^2$ in \autoref{alg:diffusion} represents the call to \autoref{alg:laplacian}.

\begin{algorithm}
    \SetKwInOut{Input}{Input}
    \SetKwInOut{Output}{Output}
    \Input{A matrix (double array) a}
    \Output{A matrix (double array) with results for the laplacian operator for each element}
    \For{$lat \in [1, nlat - 1]$}{
        \For{$lon \in [0, nlon]$}{
            $output[lat, lon] \leftarrow \frac{\Delta_x(a, lat, (lon + 1) \text{ mod } nlon) - \Delta_x(a, lat, (lon - 1) \text{ mod } nlon)}{\delta x[lat]} + \frac{\Delta_y(a, lat + 1, lon) -
            \Delta_y(a, lat - 1, lon)}{\delta y}$\;
        }
    }
    \Return{$ouput$} \;
    \caption{Calculate the laplacian operator over a matrix a}
    \label{alg:laplacian}
\end{algorithm}

\begin{algorithm}
    $\alpha_a \leftarrow 2 \cdot 10^{-5}$ \;
    $\alpha_p \leftarrow 1.5 \cdot 10^{-6}$ \;
    \While{\texttt{TRUE}}{
        $T_a \leftarrow T_a + \delta t \alpha_a \nabla^2(T_a)$ \;
        $T_p \leftarrow T_p + \delta t \alpha_p \nabla^2(T_p)$ \;
    }
    \caption{The main loop for calculating the effects of diffusion}
    \label{alg:diffusion}
\end{algorithm}

\subsection{Advection}
With thermal diffusion in place, the temperature will spread out a bit, however air is not transported yet. This means that the winds we simulate are not actually moving any air. Advection is
going to change that. Advection is a fluid flow transporting something with it as it flows. This can be temperature, gas, solids or other fluids. In our case we will be looking at temperature.
The advection equation is shown in \autoref{eq:advection}. The symbols are:

\begin{itemize}
    \item $\psi$: What is carried along (in our case temperature, $K$).
    \item $t$: The time ($s$).
    \item $u$: The fluid velocity vector ($ms^{-1}$).
    \item $\nabla$: The divergence operator (as explained in \autoref{sec:laplace}).
\end{itemize}

\begin{equation}
    \frac{\delta \psi}{\delta t} + \nabla \cdot (\psi u) = 0
    \label{eq:advection}
\end{equation}

As we expect to use the divergence operator more often throughout our model, let us define a seperate function for it in \autoref{alg:divergence}. $\Delta_x$ and $\Delta_y$ in
\autoref{alg:divergence} represents the calls to \autoref{alg:gradient x} and \autoref{alg:gradient y} respectively. We do the multiplication with the velocity vector here already, as we expect
that we might use it in combination with the divergence operator more frequently.

\begin{algorithm}
    \SetKwInOut{Input}{Input}
    \SetKwInOut{Output}{Output}
    \Input{A matrix (double array) $a$}
    \Output{A matrix (double array) containing the result of the divergence operator taken over that element}
    $dim_1 \leftarrow \text{ Length of } a \text{ in the first dimension}$ \;
    \For{$i \in [0, dim_1]$}{
        $dim_2 \leftarrow \text{ Length of } a \text{ in the second dimension (i.e. the length of the array stored at index } i)$ \;
        \For{$j \in [0, dim_2]$}{
            $output[i, j] \leftarrow \Delta_x(au, i, j) + \Delta_y(av, i, j)$ \;
        }
    }
    \Return{$output$} \;
    \caption{Calculate the result of the divergence operator on a vector}
    \label{alg:divergence}
\end{algorithm}

With the divergence functon defined, we now need to adjust \autoref{alg:diffusion} to incorporate this effect. The resulting algorithm can be found in \autoref{alg:advection}. Here $\nabla$
represents the function call to \autoref{alg:divergence}.

\begin{algorithm}
    $\alpha_a \leftarrow 2 \cdot 10^{-5}$ \;
    $\alpha_p \leftarrow 1.5 \cdot 10^{-6}$ \;
    \While{\texttt{TRUE}}{
        $T_{add} \leftarrow T_a + \delta t \alpha_a \nabla^2(T_a) + \nabla(T_a)$ \;
        $T_a \leftarrow T_a + T_{add}[5:-5, :] \text{ //Only add } T_{add} \text{ to } T_a \text{ for indices in the interval } [-nlat + 5, nlat - 5]$. \;
        $T_p \leftarrow T_p + \delta t \alpha_p \nabla^2(T_p)$ \;
    }
    \caption{The main loop for calculating the effects of advection}
    \label{alg:advection}
\end{algorithm}

Now that we have the air moving, we also need to account for the moving of the density. This is because moving air to a certain place will change the air density at that place if the air at that
place does not move away at the same rate. Say we are moving air to $x$ at $y \ ms^{-1}$. If air at $x$ moves at a rate $z \ ms^{-1}$ and $z \neq y$ then the air density at $x$ will change.
The equation we will need for that is the mass continuity equation as shown in \autoref{eq:mass continuity} \cite{masscontinue}.

\begin{equation}
    \frac{\delta \rho}{\delta t} + \nabla \cdot (\rho v) = 0
    \label{eq:mass continuity}
\end{equation}

Using this equation means that we will no longer assume that the atmosphere is incompressible. Therefore we need to change a few things in the code. First we need to change the $\rho$ in
\autoref{alg:stream3}. Since $\rho$ is no longer constant we need to access the right value of $\rho$ by specifying the indices. So $\rho$ will change to $\rho[lat, lon]$. Furthermore we need
to calculate $\rho$ after the movement of air has taken place, so we need to change \autoref{alg:advection} as well to include the calculations for $\rho$. The new version can be found in
\autoref{alg:advectionv2}. Again the $\nabla$ represents the call to \autoref{alg:divergence}.

\begin{algorithm}
    $\alpha_a \leftarrow 2 \cdot 10^{-5}$ \;
    $\alpha_p \leftarrow 1.5 \cdot 10^{-6}$ \;
    \While{\texttt{TRUE}}{
        $T_{add} \leftarrow T_a + \delta t \alpha_a \nabla^2(T_a) + \nabla(T_a)$ \;
        $T_a \leftarrow T_a + T_{add}[5:-5, :] \text{ //Only add } T_{add} \text{ to } T_a \text{ for indices in the interval } [-nlat + 5, nlat - 5]$. \;
        $\rho \leftarrow \rho + \delta t \nabla \rho$ \;
        $T_p \leftarrow T_p + \delta t \alpha_p \nabla^2(T_p)$ \;
    }
    \caption{The main loop for calculating the effects of advection}
    \label{alg:advectionv2}
\end{algorithm}

Now that we have a varying density, we need to account for that in the temperature equations. So let us do that. We need it in the denominator as the density has a direct effect on the
heat capacity of the atmosphere. The changes are reflected in \autoref{alg:temperature with density}.

\begin{algorithm}[hbt]
    \SetAlgoLined

    \While{\texttt{TRUE}}{
        \For{$lat \in [-nlat, nlat]$}{
            \For{$lon \in [0, nlot]$}{
                $T_p[lat, lon] \leftarrow T_p[lat, lon] + \frac{\delta t ((1 - a[lat, lon])S + 4\epsilon \sigma (T_a[lat, lon])^4 - 4\sigma (T_p[lat, lon])^4)}{\rho[lat, lon]C_p[lat, lon]}$ \;
                $T_a[lat, lon] \leftarrow T_a[lat, lon] + \frac{\delta t (\sigma (T_p[lat, lon])^4 - 2\epsilon\sigma (T_a[lat, lon])^4)}{\rho[lat, lon]C_a}$ \;
                $t \leftarrow t + \delta t$ \;
            }
        }
    }
    \caption{The main loop of the temperature calculations}
    \label{alg:temperature with density}
\end{algorithm}

\subsection{Improving the Coriolis Parameter}
Another change introduced is in the coriolis parameter. Up until now it has been a constant, however we know that it varies along the latitude. So let's make it vary over the latitude. Recall
\autoref{eq:coriolis}, where $\Theta$ is the latitude. Coriolis ($f$) is currently defined in \autoref{alg:gradient}, so let's incorporate the changes which are shown in \autoref{alg:coriolis}.

\begin{algorithm}
    \SetAlgoLined
    $C \leftarrow 2\pi R$ \;
    $\delta y \leftarrow \frac{C}{nlat}$ \;
    $\Omega \leftarrow 7.2921 \cdot 10^{-5}$ \;

    \For{$lat \in [-nlat, nlat]$}{
        $\delta x[lat] \leftarrow \delta y \cos(lat \cdot \frac{\pi}{180})$ \;
        $f[lat] \leftarrow 2\Omega \sin(lat \frac{\pi}{180})$ \;
    }
    \caption{Calculating the gradient $\delta x$}
    \label{alg:coriolis}
\end{algorithm}


\section{Fitting strategy}
\label{sec:fit}

\paragraph{} The estimation of CP-mixing parameter $\tilde{d}$ uses a maximum-likelihood fit performed on $m_{\gamma\gamma}$ distribution simultaneously in all 6 OO bins. The likelihood function could be constructed as:

\begin{center}
\begin{math}
\mathcal{L} = \mathcal{L}(\boldsymbol{x} |\boldsymbol{\theta}) = \prod_{bin}\prod_{j=1}^{N}f_{bin}(m_{\gamma\gamma})G(\theta)
\end{math}
\end{center}

Where $\boldsymbol x$ represents dataset, $\boldsymbol \theta$ is nuisance parameter, $f_{bin}(m_{\gamma\gamma})$ is the diphoton invariant mass distribution model for each bins, $G(\boldsymbol \theta)$ is constrain function for systematic uncertainties. The parameter of interest(POI), $\tilde{d}$, is embedded in $m_{\gamma\gamma}$ model, so there is no analytic relation between likelihood value and POI. A set of signal templates corresponding to different value of CP-mixing parameter $\tilde{d}$ is created by reweighting the SM VBF $H\to\gamma\gamma$ to build the CP-mixing models, and then a template fit could be performed with data and model with different $\tilde{d}$ value (background model keeps consistent) to evaluate the likelihood function. For the fit in each $\tilde{d}$ model, VBF signal strength, continuum background yield and parameters describing the background model are float, which means the effect of background mis-modelling is considered and any constrain from possible model-dependent cross section information is not exploited. Other nuisance parameters are fixed to their best-fit values $\hat{\boldsymbol \theta}$.

\paragraph{} A negative log-likelihood (NLL) curve can be constructed by calculating the NLL value for each $\tilde{d}$ hypothesis. Best-estimated $\tilde{d}$ as well as its central confidence interval at 68\% (95\%) confidence level (CL) can be determined with the minimum value of NLL, $NLL_{min}$ and the points at which $\Delta NLL = NLL-NLL_{min} = 0.5(1.96)$. An Asimov dataset is used to get expected sensitivity from this method and is shown in Figure ~\ref{fig:NLLcurve}.

\paragraph{} Since the Optimal Observable is the main sensitive variable for CP test in this analysis, another 2D model for OO and $m_\gamma\gamma$ was considered to construct the likelihood function. In some preliminary study this 2D model did not show significant improvement with baseline method, and had some difficulty in modelling, it was obsoleted in this analysis. Appendix ~\ref{appendix:2Dmodel} includes some results based on this.


\section{Three-state promoter model for simple repression}\label{supp_model}

In order to tackle the question of how much information the simple repression
motif can process we require the joint probability distribution of mRNA and
protein $P(m, p; t)$. To obtain this distribution we use the chemical master
equation formalism as described in \secref{sec_model}. Specifically, we assume
a three-state model where the promoter can be found 1) in a transcriptionally
active state  ($A$ state), 2) in a transcriptionally inactive state without the
repressor bound ($I$ state) and 3) with the repressor bound ($R$ state). (See
\fref{fig2_minimal_model}(A)). These three states generate a system of coupled
differential equations for each of the three state distributions $P_A(m, p)$,
$P_I(m, p)$ and $P_R(m, p)$. Given the rates shown in
\fref{fig2_minimal_model}(A) let us define the system of ODEs. For the
transcriptionally active state we have
\begin{equation}
  \begin{aligned}
    \dt{P_A(m, p)} &=
    - \overbrace{\kpoff P_A(m, p)}^{A \rightarrow I} % A -> I
    + \overbrace{\kpon P_I(m, p)}^{I \rightarrow A}\\ % I -> A
    &+ \overbrace{r_m P_A(m-1, p)}^{m-1 \rightarrow m} % m-1 -> m
    - \overbrace{r_m P_A(m, p)}^{m \rightarrow m+1}% m -> m+1
    + \overbrace{\gm (m + 1) P_A(m+1 , p)}^{m+1 \rightarrow m} % m+1 -> m
    - \overbrace{\gm m P_A(m , p)}^{m \rightarrow m-1}\\ % m -> m-1
    &+ \overbrace{r_p m P_A(m, p - 1)}^{p-1 \rightarrow p} % p-1 -> p
    - \overbrace{r_p m P_A(m, p)}^{p \rightarrow p+1} % p -> p+1
    + \overbrace{\gp (p + 1) P_A(m, p + 1)}^{p + 1 \rightarrow p} % p+1 -> p
    - \overbrace{\gp p P_A(m, p)}^{p \rightarrow p-1}. % p -> p-1
  \end{aligned}
\end{equation}
For the inactive promoter state $I$ we have
\begin{equation}
  \begin{aligned}
    \dt{P_I(m, p)} &=
    \overbrace{\kpoff P_A(m, p)}^{A \rightarrow I} % A -> I
    - \overbrace{\kpon P_I(m, p)}^{I \rightarrow A} % I -> A
    + \overbrace{\kroff P_R(m, p)}^{R \rightarrow I} % R -> I
    - \overbrace{\kron P_I(m, p)}^{I \rightarrow R}\\ % I -> R
    &+ \overbrace{\gm (m + 1) P_I(m+1 , p)}^{m+1 \rightarrow m} % m+1 -> m
    - \overbrace{\gm m P_I(m , p)}^{m \rightarrow m-1}\\ % m -> m-1
    &+ \overbrace{r_p m P_I(m, p - 1)}^{p-1 \rightarrow p} % p-1 -> p
    - \overbrace{r_p m P_I(m, p)}^{p \rightarrow p+1} % p -> p+1
    + \overbrace{\gp (p + 1) P_I(m, p + 1)}^{p + 1 \rightarrow p} % p+1 -> p
    - \overbrace{\gp p P_I(m, p)}^{p \rightarrow p-1}. % p -> p-1
  \end{aligned}
\end{equation}
And finally for the repressor bound state $R$ we have
\begin{equation}
  \begin{aligned}
    \dt{P_R(m, p)} &=
    - \overbrace{\kroff P_R(m, p)}^{R \rightarrow I} % R -> I
    + \overbrace{\kron P_I(m, p)}^{I \rightarrow R}\\ % I -> R
    &+ \overbrace{\gm (m + 1) P_R(m+1 , p)}^{m+1 \rightarrow m} % m+1 -> m
    - \overbrace{\gm m P_R(m , p)}^{m \rightarrow m-1}\\ % m -> m-1
    &+ \overbrace{r_p m P_R(m, p - 1)}^{p-1 \rightarrow p} % p-1 -> p
    - \overbrace{r_p m P_R(m, p)}^{p \rightarrow p+1} % p -> p+1
    + \overbrace{\gp (p + 1) P_R(m, p + 1)}^{p + 1 \rightarrow p} % p+1 -> p
    - \overbrace{\gp p P_R(m, p)}^{p \rightarrow p-1}. % p -> p-1
  \end{aligned}
\end{equation}

For an unregulated promoter, i.e. a promoter in a cell that has no repressors
present, and therefore constitutively expresses the gene, we use a two-state
model in which the state $R$ is not allowed. All the terms in the system of ODEs
containing $\kron$ or $\kroff$ are then set to zero.

As detailed in \secref{sec_model} it is convenient to express this system using
matrix notation \cite{Sanchez2013}. For this we define $\PP(m, p) = (P_A(m, p),
P_I(m, p), P_R(m, p))^T$. Then the system of ODEs can be expressed as
\begin{equation}
  \begin{aligned}
    \dt{\PP(m, p)} &= \Km \PP(m, p)
    - \Rm \PP(m, p) + \Rm \PP(m-1, p)
    - m \Gm \PP(m, p) + (m + 1) \Gm \PP(m + 1, p)\\
    &- m \Rp \PP(m, p) + m \Rp \PP(m, p - 1)
    - p \Gp \PP(m, p) + (p + 1) \Gp \PP(m, p + 1),
  \end{aligned}
\end{equation}
where we defined matrices representing the promoter state transition $\Km$,
\begin{align}
  \Km \equiv
  \begin{bmatrix}
    -\kpoff   & \kpon         & 0\\
    \kpoff    & -\kpon -\kron  & \kroff\\
    0         & \kron         & -\kroff
  \end{bmatrix},
\end{align}
mRNA production, $\Rm$, and degradation, $\Gm$, as
\begin{equation}
  \Rm \equiv
  \begin{bmatrix}
    r_m   & 0 & 0\\
    0     & 0 & 0\\
    0     & 0 & 0\\
  \end{bmatrix},
\end{equation}
and
\begin{equation}
  \Gm \equiv
  \begin{bmatrix}
    \gm   & 0   & 0\\
    0     & \gm & 0\\
    0     & 0   & \gm\\
  \end{bmatrix}.
\end{equation}
For the protein we also define production $\Rp$ and degradation $\Gp$ matrices
as
\begin{equation}
  \Rp \equiv
  \begin{bmatrix}
    r_p   & 0   & 0\\
    0     & r_p & 0\\
    0     & 0   & r_p\\
  \end{bmatrix}
\end{equation}
and
\begin{equation}
  \Gp \equiv
  \begin{bmatrix}
    \gp   & 0   & 0\\
    0     & \gp & 0\\
    0     & 0   & \gp\\
  \end{bmatrix}.
\end{equation}

The corresponding equation for the unregulated two-state promoter takes the
exact same form with the definition of the matrices following the same scheme
without including the third row and third column, and setting $\kron$ and
$\kroff$ to zero.

A closed-form solution for this master equation might not even exist. The
approximate solution of chemical master equations of this kind is an active
area of research. As we will see in \siref{supp_param_inference} the two-state
promoter master equation has been analytically solved for the mRNA
\cite{Peccoud1995} and protein distributions \cite{Shahrezaei2008}. For our
purposes, in \siref{supp_maxent} we will detail how to use the Maximum Entropy
principle to approximate the full distribution for the two- and three-state
promoter.

\section{Parameter inference}\label{supp_param_inference}

(Note: The Python code used for the calculations presented in this section can
be found in the
\href{https://www.rpgroup.caltech.edu//chann_cap/software/chemical_master_mRNA_FISH_mcmc.html}{following
link} as an annotated Jupyter notebook)

With the objective of generating falsifiable predictions with meaningful
parameters, we infer the kinetic rates for this three-state promoter model
using different data sets generated in our lab over the last decade concerning
different aspects of the regulation of the simple repression motif. For
example, for the unregulated promoter transition rates $\kpon$ and $\kpoff$ and
the mRNA production rate $r_m$, we use single-molecule mRNA FISH counts from an
unregulated promoter \cite{Jones2014a}. Once these parameters are fixed, we use
the values to constrain the repressor rates $\kron$ and $\kroff$. These
repressor rates are obtained using information from mean gene expression
measurements from bulk LacZ colorimetric assays \cite{Garcia2011c}. We also
expand our model to include the allosteric nature of the repressor protein,
taking advantage of video microscopy measurements done in the context of
multiple promoter copies \cite{Brewster2014} and flow-cytometry measurements of
the mean response of the system to different levels of induction
\cite{Razo-Mejia2018}. In what follows of this section we detail the steps
taken to infer the parameter values. At each step the values of the parameters
inferred in previous steps constrain the values of the parameters that are not
yet determined, building in this way a self-consistent model informed by work
that spans several experimental techniques.

\subsection{Unregulated promoter rates}

We begin our parameter inference problem with the promoter on and off rates
$\kpon$ and $\kpoff$, as well as the mRNA production rate $r_m$. In this case
there are only two states available to the promoter -- the inactive state $I$
and the transcriptionally active  state $A$. That means that the third ODE for
$P_R(m, p)$ is removed from the system. The mRNA steady state distribution for
this particular two-state promoter model was solved analytically by Peccoud and
Ycart \cite{Peccoud1995}. This distribution $P(m) \equiv P_I(m) + P_A(m)$ is of
the form
\begin{equation}
  \small
  P(m \mid \kpon, \kpoff, r_m, \gm) =
  {\Gamma \left( \frac{\kpon}{\gm} + m \right) \over
  \Gamma(m + 1) \Gamma\left( \frac{\kpoff+\kpon}{\gm} + m \right)}
  {\Gamma\left( \frac{\kpoff+\kpon}{\gm} \right) \over
  \Gamma\left( \frac{\kpon}{\gm} \right)}
  \left( {r_m \over \gm} \right)^m
  F_1^1 \left( {\kpon \over \gm} + m,
  {\kpoff + \kpon \over \gm} + m,
  -{r_m \over \gm} \right),
  \label{seq_two_state_mRNA}
\end{equation}
where $\Gamma(\cdot)$ is the gamma function, and $F_1^1$ is the confluent
hypergeometric function of the first kind. This rather complicated expression
will aid us to find parameter values for the rates. The inferred rates $\kpon$,
$\kpoff$ and $r_m$ will be expressed in units of the mRNA degradation rate
$\gm$. This is because the model in \eref{seq_two_state_mRNA} is homogeneous in
time, meaning that if we divide all rates by a constant it would be equivalent
to multiplying the characteristic time scale by the same constant. As we will
discuss in the next section, \eref{seq_two_state_mRNA} has degeneracy in the
parameter values. What this means is that a change in one of the parameters,
specifically $r_m$, can be compensated by a change in another parameter,
specifically $\kpoff$, to obtain the exact same distribution. To work around
this intrinsic limitation of the model we will include in our inference prior
information from what we know from equilibrium-based models.

\subsubsection*{Bayesian parameter inference of RNAP rates}

In order to make progress at inferring the unregulated promoter state
transition rates, we make use of the single-molecule mRNA FISH data from Jones
et al. \cite{Jones2014a}. \fref{sfig_lacUV5_FISH} shows the distribution of mRNA per cell
for the \textit{lacUV5} promoter used for our inference. This
promoter, being very strong, has a mean copy number of $\ee{m} \approx 18$
mRNA/cell.

\begin{figure}[h!]
	\centering \includegraphics
  {../fig/si/figS01.pdf}
	\caption{\textbf{\textit{lacUV5} mRNA per cell distribution.} Data from
	\cite{Jones2014a} of the unregulated \textit{lacUV5} promoter as inferred
	from single molecule mRNA FISH.}
  \label{sfig_lacUV5_FISH}
\end{figure}

Having this data in hand we now turn to Bayesian parameter inference. Writing
Bayes theorem we have
\begin{equation}
  P(\kpon, \kpoff, r_m \mid D) = {P(D \mid \kpon, \kpoff, r_m)
  P(\kpon, \kpoff, r_m) \over P(D)},
  \label{seq_bayes_rnap_rates}
\end{equation}
where $D$ represents the data. For this case the data consists of single-cell
mRNA counts $D = \{ m_1, m_2, \ldots, m_N \}$, where $N$ is the number of
cells. We assume that each cell's measurement is independent of the others such
that we can rewrite \eref{seq_bayes_rnap_rates} as
\begin{equation}
  P(\kpon, \kpoff, r_m \mid \{m_i\}) \propto
  \left[\prod_{i=1}^N P(m_i \mid \kpon, \kpoff, r_m) \right]
  P(\kpon, \kpoff, r_m),
  \label{seq_bayes_sample}
\end{equation}
where we ignore the normalization constant $P(D)$. The likelihood term $P(m_i
\mid \kpon, \kpoff, r_m)$ is exactly given by \eref{seq_two_state_mRNA} with
$\gm = 1$. Given that we have this functional form for the distribution, we can
use Markov Chain Monte Carlo (MCMC) sampling to explore the 3D parameter space
in order to fit \eref{seq_two_state_mRNA} to the mRNA-FISH data.

\subsubsection*{Constraining the rates given prior thermodynamic knowledge.}

One of the strengths of the Bayesian approach is that we can include all the
prior knowledge on the parameters when performing an inference
\cite{MacKay2003}. Basic features such as the fact that the rates have to be
strictly positive constrain the values that these parameters can take. For
the specific rates analyzed in this section we know more than the simple
constraint of non-negative values. The expression of an unregulated promoter has
been studied from a thermodynamic perspective \cite{Brewster2012}. Given the
underlying assumptions of these equilibrium models, in which the probability of
finding the RNAP bound to the promoter is proportional to the transcription
rate \cite{Bintu2005a}, they can only make statements about the mean expression
level. Nevertheless if both the thermodynamic and the kinetic model describe
the same process, the predictions for the mean gene expression level must
agree. That means that we can use what we know about the mean gene expression,
and how this is related to parameters such as molecule copy numbers and binding
affinities, to constrain the values that the rates in question can take.

In the case of this two-state promoter it can be shown that the mean number of
mRNA is given by \cite{Sanchez2013} (See \siref{supp_moments} for moment
computation)
\begin{equation}
  \ee{m} = {r_m \over \gm} {\kpon \over \kpon + \kpoff}.
  \label{seq_mean_kinetic}
\end{equation}
Another way of expressing this is as ${r_m \over \gm} \times
p_{\text{active}}^{(p)}$, where $p_{\text{active}}^{(p)}$ is the probability of
the promoter being in the transcriptionally active state. The thermodynamic
picture has an equivalent result where the mean number of mRNA is given by
\cite{Brewster2012, Bintu2005a}
\begin{equation}
  \left\langle m \right\rangle = {r_m \over \gm}
  {{P \over \Nns} e^{-\beta\eP}
  \over 1 + {P \over \Nns} e^{-\beta\eP}},
  \label{seq_mean_thermo}
\end{equation}
where $P$ is the number of RNAP per cell, $\Nns$ is the number of non-specific
binding sites, $\eP$ is the RNAP binding energy in $k_BT$ units and $\beta\equiv
{(k_BT)}^{-1}$. Using \eref{seq_mean_kinetic} and \eref{seq_mean_thermo} we can
easily see that if these frameworks are to be equivalent, then it must be true
that
\begin{equation}
  {\kpon \over \kpoff} = {P \over \Nns} e^{-\beta\eP},
\end{equation}
or equivalently
\begin{equation}
  \ln \left({\kpon \over \kpoff}\right) =
  -\beta\eP + \ln P - \ln \Nns.
\end{equation}
To put numerical values into these variables we can use information from the
literature. The RNAP copy number is order $P \approx 1000-3000$ RNAP/cell for a
1 hour doubling time \cite{Klumpp2008}. As for the number of non-specific
binding sites and the binding energy, we have that $\Nns = 4.6\times 10^6$
\cite{Bintu2005a} and $-\beta\eP \approx 5 - 7$ \cite{Brewster2012}. Given
these values we define a Gaussian prior for the log ratio of these two
quantities of the form
\begin{equation}
  P\left(\ln \left({\kpon \over \kpoff}\right) \right) \propto
  \exp \left\{ - {\left(\ln \left({\kpon \over \kpoff}\right) -
  \left(-\beta\eP + \ln P - \ln \Nns \right) \right)^2
  \over 2 \sigma^2} \right\},
  \label{seq_prior_single}
\end{equation}
where $\sigma$ is the variance that accounts for the uncertainty in these
parameters. We include this prior as part of the prior term $P(\kpon, \kpoff,
r_m)$ of \eref{seq_bayes_sample}. We then use MCMC to sample out of the
posterior distribution given by \eref{seq_bayes_sample}. \fref{sfig_mcmc_rnap}
shows the MCMC samples of the posterior distribution. For the case of the
$\kpon$ parameter there is a single symmetric peak. $\kpoff$ and $r_m$ have a
rather long tail towards large values. In fact, the 2D projection of $\kpoff$ vs
$r_m$ shows that the model is sloppy, meaning that the two parameters are highly
correlated. This feature is a common problem for many non-linear systems used in
biophysics and systems biology \cite{Transtrum2015}. What this implies is that
we can change the value of $\kpoff$, and then compensate by a change in $r_m$ in
order to maintain the shape of the mRNA distribution. Therefore it is impossible
from the data and the model themselves to narrow down a single value for the
parameters. Nevertheless since we included the prior information on the rates as
given by the analogous form between the equilibrium and non-equilibrium
expressions for the mean mRNA level, we obtained a more constrained parameter
value for the RNAP rates and the transcription rate that we will take as the
peak of this long-tailed distribution.

\begin{figure}[h!]
	\centering \includegraphics
  {../fig/si/figS02.pdf}
	\caption{\textbf{MCMC posterior distribution.} Sampling out of
	\eref{seq_bayes_sample} the plot shows 2D and 1D projections of the 3D
	parameter space. The parameter values are (in units of the mRNA degradation
	rate $\gm$) $\kpon = 4.3^{+1}_{-0.3}$, $\kpoff = 18.8^{+120}_{-10}$ and $r_m =
	103.8^{+423}_{-37}$ which are the modes of their respective distributions,
	where the superscripts and subscripts represent the upper and lower bounds of
	the 95$^\text{th}$ percentile of the parameter value distributions}
  \label{sfig_mcmc_rnap}
\end{figure}

The inferred values $\kpon = 4.3^{+1}_{-0.3}$, $\kpoff = 18.8^{+120}_{-10}$
and $r_m = 103.8^{+423}_{-37}$ are given in units of the mRNA degradation
rate $\gm$. Given the asymmetry of the parameter distributions we report the
upper and lower bound of the 95$^\text{th}$ percentile of the posterior
distributions. Assuming a mean life-time for mRNA of $\approx$ 3 min (from
this
\href{http://bionumbers.hms.harvard.edu/bionumber.aspx?&id=107514&ver=1&trm=mRNA%20mean%20lifetime}{link})
we have an mRNA degradation rate of $\gm \approx 2.84 \times 10^{-3} s^{-1}$.
Using this value gives the following values for the inferred rates: $\kpon =
0.024_{-0.002}^{+0.005} s^{-1}$, $\kpoff = {0.11}_ {-0.05}^{+0.66} s^{-1}$, and
$r_m = 0.3_{-0.2}^{+2.3} s^{-1}$.

\fref{sfig_lacUV5_theory_data} compares the experimental data from
\fref{sfig_lacUV5_FISH} with the resulting distribution obtained by substituting
the most likely parameter values into \eref{seq_two_state_mRNA}. As we can see
this two-state model fits the data adequately.

\begin{figure}[h!]
	\centering \includegraphics[width=0.5\columnwidth]
  {../fig/si/figS03.pdf}
	\caption{\textbf{Experimental vs. theoretical distribution of mRNA per cell
  using parameters from Bayesian inference.} Dotted line shows the result of
  using \eref{seq_two_state_mRNA} along with the parameters inferred for the
  rates. Blue bars are the same data as \fref{sfig_lacUV5_FISH} obtained from
  \cite{Jones2014a}.}
  \label{sfig_lacUV5_theory_data}
\end{figure}

\subsection{Accounting for variability in the number of promoters}

As discussed in ref. \cite{Jones2014a} and further expanded in
\cite{Peterson2015} an important source of cell-to-cell variability in gene
expression in bacteria is the fact that, depending on the growth rate and the
position relative to the chromosome replication origin, cells can have multiple
copies of any given gene. Genes closer to the replication origin have on
average higher gene copy number compared to genes at the opposite end. For the
locus in which our reporter construct is located (\textit{galK}) and the
doubling time of the mRNA FISH experiments we expect to have $\approx$ 1.66
copies of the gene \cite{Jones2014a, Bremer1996}. This implies that the cells
spend 2/3 of the cell cycle with two copies of the promoter and the rest with a
single copy.

To account for this variability in gene copy we extend the model assuming that
when cells have two copies of the promoter the mRNA production rate is $2 r_m$
compared to the rate $r_m$ for a single promoter copy. The probability of
observing a certain mRNA copy $m$ is therefore given by
\begin{equation}
  P(m) = P(m \mid \text{one promoter}) \cdot P(\text{one promoter}) +
  P(m \mid \text{two promoters}) \cdot P(\text{two promoters}).
  \label{seq_prob_multipromoter}
\end{equation}
Both terms $P(m \mid \text{promoter copy})$ are given by
\eref{seq_two_state_mRNA} with the only difference being the rate $r_m$. It is
important to acknowledge that \eref{seq_prob_multipromoter} assumes that once
the gene is replicated the time scale in which the mRNA count relaxes to the
new steady state is much shorter than the time that the cells spend in this two
promoter copies state. This approximation should be valid for a short lived
mRNA molecule, but the assumption is not applicable for proteins whose
degradation rate is comparable to the cell cycle length as explored in
\secref{sec_cell_cycle}.

In order to repeat the Bayesian inference including this variability in gene
copy number we must split the mRNA count data into two sets -- cells with a
single copy of the promoter and cells with two copies of the promoter. For the
single molecule mRNA FISH data there is no labeling of the locus, making it
impossible to determine the number of copies of the promoter for any given
cell. We therefore follow Jones et al. \cite{Jones2014a} in using the cell area
as a proxy for stage in the cell cycle. In their approach they sorted cells by
area, considering cells below the 33$\th$ percentile having a single promoter
copy and the rest as having two copies. This approach ignores that cells are
not uniformly distributed along the cell cycle. As first derived in
\cite{Powell1956} populations of cells in a log-phase are exponentially
distributed along the cell cycle. This distribution is of the form
\begin{equation}
P(a) = (\ln 2) \cdot 2^{1 - a},
\label{seq_cell_cycle_dist}
\end{equation}
where $a \in [0, 1]$ is the stage of the cell cycle, with $a = 0$ being the
start of the cycle and $a = 1$ being the cell division (See
\siref{supp_cell_age_dist} for a derivation of \eref{seq_cell_cycle_dist}).
\fref{sfig_cell_area} shows the separation of the two groups based on area
where \eref{seq_cell_cycle_dist} was used to weight the distribution along the
cell cycle.

\begin{figure}[h!]
	\centering \includegraphics
  {../fig/si/figS04.pdf}
	\caption{\textbf{Separation of cells based on cell size.} Using the area as
  a proxy for position in the cell cycle, cells can be sorted into two groups --
  small cells (with one promoter copy) and large cells (with two promoter
  copies). The vertical black line delimits the threshold that divides both
  groups as weighted by \eref{seq_cell_cycle_dist}.}
  \label{sfig_cell_area}
\end{figure}

A subtle, but important consequence of \eref{seq_cell_cycle_dist} is that
computing any quantity for a single cell is not equivalent to computing the same
quantity for a population of cells. For example, let us assume that we want to
compute the mean mRNA copy number $\ee{m}$. For a single cell this would be of
the form
\begin{equation}
  \ee{m}_{\text{cell}} = \ee{m}_1 \cdot f + \ee{m}_2 \cdot (1 - f),
\end{equation}
where $\ee{m}_i$ is the mean mRNA copy number with $i$ promoter copies in the
cell, and $f$ is the fraction of the cell cycle that cells spend with a single
copy of the promoter. For a single cell the probability of having a single
promoter copy is equivalent to this fraction $f$. But \eref{seq_cell_cycle_dist}
tells us that if we sample unsynchronized cells we are not sampling uniformly
across the cell cycle. Therefore for a population of cells the mean mRNA is
given by
\begin{equation}
  \ee{m}_{\text{population}} = \ee{m}_1 \cdot \phi + \ee{m}_2 \cdot (1 - \phi)
  \label{seq_mean_m_pop}
\end{equation}
where the probability of sampling a cell with one promoter $\phi$ is given by
\begin{equation}
  \phi = \int_0^f P(a) da,
\end{equation}
where $P(a)$ is given by \eref{seq_age_prob}. What this equation computes is
the probability of sampling a cell during a stage of the cell cycle $< f$ where
the reporter gene hasn't been replicated yet. \fref{sfig_mRNA_by_size} shows the
distribution of both groups. As expected larger cells have a higher mRNA copy
number on average.

\begin{figure}[h!]
	\centering \includegraphics
  {../fig/si/figS05.pdf}
	\caption{\textbf{mRNA distribution for small and large cells.} (A) histogram
	and (B) cumulative distribution function of the small and large cells as
	determined in \fref{sfig_cell_area}. The triangles above histograms in (A)
	indicate the mean mRNA copy number for each group.}
  \label{sfig_mRNA_by_size}
\end{figure}

We modify \eref{seq_bayes_sample} to account for the two separate groups of
cells. Let $N_s$ be the number of cells in the small size group and $N_l$ the
number of cells in the large size group. Then the posterior distribution for the
parameters is of the form
\begin{equation}
  \small
P(\kpon, \kpoff, r_m \mid \{m_i\}) \propto
  \left[\prod_{i=1}^{N_s} P(m_i \mid \kpon, \kpoff, r_m)\right]
  \left[\prod_{j=1}^{N_l} P(m_j \mid \kpon, \kpoff, 2 r_m)\right]
  P(\kpon, \kpoff, r_m),
  \label{seq_bayes_sample_double}
\end{equation}
where we split the product of small and large cells.

For the two-promoter model the prior shown in \eref{seq_prior_single} requires a
small modification. \eref{seq_mean_m_pop} gives the mean mRNA copy number of a
population of asynchronous cells growing at steady state. Given that we assume
that the only difference between having one vs. two promoter copies is the
change in transcription rate from $r_m$ in the single promoter case to $2 r_m$
in the two-promoter case we can
write \eref{seq_mean_m_pop} as
\begin{equation}
  \ee{m} = \phi \cdot {r_m \over \gm} {\kpon \over \kpon + \kpoff} +
      (1 -\phi) \cdot {2 r_m \over \gm} {\kpon \over \kpon + \kpoff}.
\end{equation}
This can be simplified to
\begin{equation}
  \ee{m} = (2 - \phi) {r_m \over \gm} {\kpon \over \kpon + \kpoff}.
  \label{seq_mean_m_double_rates}
\end{equation}

Equating \eref{seq_mean_m_double_rates} and \eref{seq_mean_thermo} to again
require self-consistent predictions of the mean mRNA from the equilibrium and
kinetic models gives
\begin{equation}
  (2 - \phi) {\kpon \over \kpon + \kpoff} =
  {{P \over \Nns} e^{-\beta\eP}
  \over 1 + {P \over \Nns} e^{-\beta\eP}}.
\end{equation}
Solving for $\kpon \over \kpoff$ results in
\begin{equation}
  \left({\kpon \over \kpoff}\right) =
  {\rho \over \left[ (1 + \rho)(2 - \phi) - \rho \right]},
  \label{seq_kinetic_thermo_equiv}
\end{equation}
where we define $\rho \equiv {P \over \Nns} e^{-\beta\eP}$. To simplify things
further we notice that for the specified values of $P = 1000 - 3000$ per cell,
$\Nns = 4.6 \times 10^6$ bp, and $-\beta\eP = 5 - 7$, we can safely assume that
$\rho \ll 1$. This simplifying assumption has been previously called the weak
promoter approximation \cite{Garcia2011c}. Given this we can simplify
\eref{seq_kinetic_thermo_equiv} as
\begin{equation}
  {\kpon \over \kpoff} = {1 \over 2 - \phi} {P \over \Nns} e^{-\beta\eP}.
\end{equation}
Taking the log of both sides gives
\begin{equation}
  \ln\left({\kpon \over \kpoff}\right) = -\ln (2 - \phi) + \ln P - \ln\Nns
  - \beta\eP.
\end{equation}
With this we can set as before a Gaussian prior to constrain the ratio of the
RNAP rates as
\begin{equation}
  P\left(\ln \left({\kpon \over \kpoff}\right) \right)  \propto
  \exp \left\{ - {\left(\ln \left({\kpon \over \kpoff}\right) -
  \left[-\ln(2 - \phi) -\beta\eP + \ln P - \ln \Nns \right) \right]^2
  \over 2 \sigma^2} \right\}.
  \label{seq_prior_double}
\end{equation}

\fref{sfig_mcmc_rnap_double} shows the result of sampling out of
\eref{seq_bayes_sample_double}. Again we see that the model is highly sloppy
with large credible regions obtained for $\kpoff$ and $r_m$. Nevertheless,
again the use of the prior information allows us to get a parameter values
consistent with the equilibrium picture.

\begin{figure}[h!]
	\centering \includegraphics
  {../fig/si/figS06.pdf}
	\caption{\textbf{MCMC posterior distribution for a multi-promoter model.}
	Sampling out of \eref{seq_bayes_sample_double} the plot shows 2D and 1D
	projections of the 3D parameter space. The parameter values are (in units of
	the mRNA degradation rate $\gm$) $\kpon = 6.4^{+0.8}_{-0.4}$, $\kpoff =
	132^{+737}_{-75}$ and $r_m = 257^{+1307}_{-132}$ which are the modes of
	their respective distributions, where the superscripts and subscripts
	represent the upper and lower bounds of the 95$\th$ percentile of the
	parameter value distributions. The sampling was bounded to values $<$ 1000 for
  numerical stability when computing the confluent hypergeometric function.}
  \label{sfig_mcmc_rnap_double}
\end{figure}

Using again a mRNA mean lifetime of $\approx 3$ min gives the following
values for the parameters: $\kpon = {0.03}_{-0.002}^{+0.004} s^{-1}$, $\kpoff =
{0.7} _{-0.4}^{+4.1} s^{-1}$, and $r_m = {1.4}_{-0.7}^{+7.3} s^{-1}$.
\fref{sfig_lacUV5_theory_data_double} shows the result of applying
\eref{seq_prob_multipromoter} using these parameter values. Specifically
\fref{sfig_lacUV5_theory_data_double}(A) shows the global distribution
including cells with one and two promoters and
\fref{sfig_lacUV5_theory_data_double}(B) splits the distributions within the
two populations. Given that the model adequately describes both populations
independently and pooled together we confirm that using the cell area as a
proxy for stage in the cell cycle and the doubling of the transcription rate
once cells have two promoters are reasonable approximations.

\begin{figure}[h!]
	\centering \includegraphics
  {../fig/si/figS07.pdf}
  \caption{\textbf{Experimental vs. theoretical distribution of mRNA per cell
  using parameters for multi-promoter model.} (A) Solid line shows the result
  of using \eref{seq_prob_multipromoter} with the parameters inferred by
  sampling \eref{seq_bayes_sample_double}. Blue bars are the same data as
  \fref{sfig_lacUV5_FISH} from \cite{Jones2014a}. (B) Split distributions of
  small cells (light blue bars) and large cells (dark blue) with the
  corresponding theoretical predictions with transcription rate $r_m$ (light
  blue line) and transcription rate $2 r_m$ (dark blue line)}
	\label{sfig_lacUV5_theory_data_double}
\end{figure}

It is hard to make comparisons with literature reported values because these
kinetic rates are effective parameters hiding a lot of the complexity of
transcription initiation \cite{Browning2004}. Also the non-identifiability of
the parameters restricts our explicit comparison of the actual numerical values
of the inferred rates. Nevertheless from the model we can see that the mean
burst  size for each transcription event is given by $r_m / \kpoff$. From our
inferred values we obtain then a mean burst size of $\approx 1.9$ transcripts
per cell. This is similar to the reported burst size of 1.15 on a similar
system on \textit{E. coli} \cite{Yu2006}.

\subsection{Repressor rates from three-state regulated promoter.}

Having determined the unregulated promoter transition rates we now proceed to
determine the repressor rates $\kron$ and $\kroff$. The values of these rates
are constrained again by the correspondence between our kinetic picture and
what we know from equilibrium models \cite{Phillips2015}. For this analysis we
again exploit the feature that, at the mean, both the kinetic language and the
thermodynamic language should have equivalent predictions. Over the last decade
there has been great effort in developing equilibrium models for gene
expression regulation \cite{Buchler2003, Vilar2011, Bintu2005a}. In particular
our group has extensively characterized the simple repression motif using this
formalism \cite{Garcia2011c, Brewster2014, Razo-Mejia2018}.

The dialogue between theory and experiments has led to simplified expressions
that capture the phenomenology of the gene expression response as a function of
natural variables such as molecule count and affinities between molecular
players. A particularly interesting quantity for the simple repression motif
used by Garcia \& Phillips \cite{Garcia2011c} is the fold-change in gene
expression, defined as
\begin{equation}
  \foldchange = {\ee{\text{gene expression}(R \neq 0)} \over
                 \ee{\text{gene expression}(R = 0)}},
\end{equation}
where $R$ is the number of repressors per cell and $\ee{\cdot}$ is the
population average. The fold-change is simply the mean expression level in the
presence of the repressor relative to the mean expression level in the absence
of regulation. In the language of statistical mechanics this quantity takes the
form
\begin{equation}
  \foldchange = \left( 1 + {R \over \Nns} e^{-\beta\eR} \right)^{-1},
  \label{seq_fc_thermo}
\end{equation}
where $\eR$ is the repressor-DNA binding energy, and as before $\Nns$ is the
number of non-specific binding sites where the repressor can bind
\cite{Garcia2011c}.

To compute the fold-change in the chemical master equation language we compute
the first moment of the steady sate mRNA distribution $\ee{m}$ for both the
three-state promoter ($R \neq 0$) and the two-state promoter case ($R=0$) (See
\siref{supp_moments} for moment derivation). The unregulated (two-state)
promoter mean mRNA copy number is given by \eref{seq_mean_m_double_rates}. For
the regulated (three-state) promoter we have an equivalent expression of the
form
\begin{equation}
  \ee{m (R \neq 0)} = (2 - \phi){r_m \over \gm} {\kroff\kpon
  \over \kpoff\kroff + \kpoff\kron + \kroff\kpon}.
\end{equation}
Computing the fold-change then gives
\begin{equation}
  \foldchange = {\ee{m (R \neq 0)} \over \ee{m (R = 0)}} =
  {\kroff \left( \kpoff + \kpon \right) \over
  \kpoff\kron + \kroff \left( \kpoff + \kpon \right)},
  \label{seq_fold_change_cme}
\end{equation}
where the factor $(2 - \phi)$ due to the multiple promoter copies, the
transcription rate $r_m$ and the mRNA degradation rate $\gm$ cancel out.

Given that the number of repressors per cell $R$ is an experimental variable
that we can control, we assume that the rate at which the promoter transitions
from the transcriptionally inactive state to the repressor bound state,
$\kron$, is given by the concentration of repressors $[R]$ times a diffusion
limited on rate $k_o$. For the diffusion limited constant $k_o$ we use the
value used by Jones et al. \cite{Jones2014a}. With this in hand we can rewrite
\eref{seq_fold_change_cme} as
\begin{equation}
  \foldchange = \left( 1 + {k_0 [R] \over \kroff}
                {\kpoff \over \kpon + \kpoff} \right)^{-1}.
  \label{seq_fc_kinetic}
\end{equation}

We note that both \eref{seq_fc_thermo} and \eref{seq_fc_kinetic} have the same
functional form. Therefore if both languages predict the same output for the
mean gene expression level, it must be true that
\begin{equation}
  {k_o [R] \over \kroff}{\kpoff \over \kpon + \kpoff} =
  {R \over \Nns} e^{-\beta\eR}.
\end{equation}
Solving for $\kroff$ gives
\begin{equation}
  \kroff = {k_o [R] \Nns e^{\beta\eR} \over R}{\kpoff \over \kpon + \kpoff}.
  \label{seq_kroff_complete}
\end{equation}

Since the reported value of $k_o$ is given in units of nM$^{-1}$s$^{-1}$ in
order for the units to cancel properly the repressor concentration has to be
given in nM rather than absolute count. If we consider that the repressor
concentration is equal to
\begin{equation}
[R] = \frac{R}{V_{cell}}\cdot \frac{1}{N_A},
\end{equation}
where $R$ is the absolute repressor copy number per cell, $V_{cell}$ is the cell
volume and $N_A$ is Avogadro's number. The \textit{E. coli} cell volume is 2.1
fL \cite{Radzikowski2016}, and Avogadro's number is $6.022 \times 10^{23}$. If
we further include the conversion factor to turn M into nM we find that
\begin{equation}
[R] = {R \over 2.1 \times 10^{-15} L} \cdot {1 \over 6.022 \times 10^{23}}
\cdot {10^9 \text{ nmol} \over 1 \text{ mol}} \approx 0.8 \times R.
\end{equation}
Using this we simplify \eref{seq_kroff_complete} as
\begin{equation}
  \kroff \approx 0.8 \cdot k_o \cdot \Nns e^{\beta\eR}
   \cdot {\kpoff \over \kpon + \kpoff}.
  \label{seq_kroff}
\end{equation}
What \eref{seq_kroff} shows is the direct relationship that must be satisfied if
the equilibrium model is set to be consistent with the non-equilibrium kinetic
picture. \tref{stab_koff} summarizes the values obtained for the three operator
sequences used throughout this work. To compute these numbers the number of
non-specific binding sites $\Nns$ was taken to be $4.6 \times 10^6$ bp, i.e. the
size of the {\it E. coli} K12 genome.

\begin{table}[]
  \caption{\textbf{Binding sites and corresponding parameters.}}
\begin{tabular}{|c|c|c|}
\hline
 Operator & $\eR\; (k_BT)$ & $\kroff \; (s^{-1})$  \\ \hline
 O1 & -15.3 & 0.002  \\ \hline
 O2 & -13.9 & 0.008  \\ \hline
 O3 & -9.7  & 0.55   \\ \hline
\end{tabular}
\label{stab_koff}
\end{table}

{\it In-vivo} measurements of the Lac repressor off rate have been done with
single-molecule resolution \cite{Hammar2014}. The authors report a mean
residence time of $5.3 \pm 0.2$ minutes for the repressor on an O1 operator. The
corresponding rate is $\kroff \approx 0.003$ $(s^{-1})$, very similar value to
what we inferred from our model. In this same reference the authors determined
that on average the repressor takes $30.9 \pm 0.5$ seconds to bind to the
operator \cite{Hammar2014}. Given the kinetic model presented in
\fref{fig2_minimal_model}(A) this time can be converted to the probability of
not being on the repressor bound state $P_{\text{not }R}$. This is computed as
\begin{equation}
  P_{\text{not }R} = {\tau_{\text{not }R} \over
                      \tau_{\text{not }R} + \tau_{R}},
\end{equation}
where $\tau_{\text{not }R}$ is the average time that the operator is not
occupied by the repressor and $\tau_{R}$ is the average time that the repressor
spends bound to the operator. Substituting the numbers from \cite{Hammar2014}
gives $P_{\text{not }R} \approx 0.088$. From our model we can compute the zeroth
moment $\ee{m^0 p^0}$ for each of the three promoter states. This moment is
equivalent to the probability of being on each of the promoter states. Upon
substitution of our inferred rate parameters we can compute $P_{\text{not }R}$
as
\begin{equation}
  P_{\text{not }R} = 1 - P_R \approx 0.046,
\end{equation}
where $P_R$ is the probability of the promoter being bound by the repressor. The
value we obtained is within a factor of two from the one reported in
\cite{Hammar2014}.


\chapter{Introduction}
In Nashian game theory, we ask ourselves, if a rational player knew the strategies of all opponent players, what strategy would she choose.
It would naturally be the strategy that maximizes her own payoff.
A state in which no player would be motivated to change strategy even after knowing the opponents' strategies is called a Nash equilibrium.

Non-Nashian game theory offers a different perspective.
It assumes that each player can perfectly predict the other players' strategies.
Let us think about what this means for an individual player (let us call her Alice).
When thinking about this assumption for the first time, it might seem evident that Alice should choose whatever strategy yields the best payoff for her, considering all the opponents' strategies that she correctly predicted, i.e., the Nashian best response.
However, since the other players can also predict perfectly, they would know about this and adapt their strategies accordingly.
If Alice acts rationally, she needs to take this into consideration (and the other players need to take her consideration into theirs, and she needs to take theirs into hers, and so on).

For example, consider the famous Prisonner's dilemma (see \autoref{tab:prisoners-dilemma}).
Suppose that Alice was playing against Bob and predicted that Bob was going to cooperate.
Her Nahian best response would then be to defect.
If she did choose to defect, though, Bob would predict it, and he would defect as well.
This would lead to a Nash equilibrium (defect, defect).
However, it is not a PTE because Alice knew about this from the beginning, and she must have also considered the other case---cooperating.
If Alice cooperated, we can see by the same argument (since the game is symmetric) that Bob would not be motivated to switch from cooperating to defecting.
This is because if he defected, Alice would defect as well, and (cooperate, cooperate) has a strictly better payoff for Bob than (defect, defect).
Thus, both Alice and Bob know that cooperating leads to the best possible payoff for each, and (cooperate, cooperate) indeed is a PTE.

\begin{table}
	\caption{Prisoner's dilemma---a classic example of a game in normal form.
	Two suspects (row- and column-player) are placed in solitary confinement, with no means of communicating with each other.
	Each suspect has two options: either cooperate with the other by remaining silent or defect by testifying against the other.
	If both suspects remain silent, each of them will serve one year in prison.
	If one defects while the other remains silent, the other will serve three years.
	If both defect, each of them will serve two years.
	}
	\label{tab:prisoners-dilemma}
	\centering
	\begin{tabular}{|c|c|c|}
	  \hline
				& cooperate & defect \\
	  \hline
	  cooperate & -1, -1    & -3, 0  \\
	  \hline
	  defect    & 0, -3     & -2, -2 \\
	  \hline
	\end{tabular}
  \end{table}

Note that Alice and Bob actually never needed to use their predicting ability at all.
Just believing that the other player can predict their strategy was enough to choose an optimal strategy.
This is, in fact, true for any game, not just for our example \cite{Fourny20}.

In general, games can have multiple PTEs.
However, games without ties (sometimes also called games in general position), if a PTE exists, then it is unique, and Pareto-optimal \cite{Fourny20}.

We provide a formal definition of PTE in \autoref{chap:background}, but this should be enough to illustrate why it is an interesting concept.
Since game theory has many practical applications, it is interesting to research how or under what conditions players might be motivated to play in a way that leads to everyone reaching as good a payoff as possible.

The goal of this work is twofold:
First, it is to develop a REST API-based backend for playing games that will teach human players to think similarly to Alice and Bob in our example.
That is, it will motivate them to play towards a PTE (or at least to something that is, in some sense, close to a PTE).
We develop a backend that supports playing 2-player randomly generated games against a computer.
The backend is in the form of a web (HTTP) based API server.
Any client can connect to the server and request a new game which the server generates randomly and sends back to the client.
Subsequently, the client can choose a strategy and send it to the server, which responds with the computer's strategy and the resulting payoff.
The human player is supposed to think that the computer chooses its strategy independently, not knowing the opponent's strategy, even though in reality, it actually does know the opponent's strategy and uses it to choose its own---this is to simulate Perfect Prediction.
All games played through the server are stored in a database together with the results to be analyzed later.

The second goal is to define new notions of best responses that might ultimately lead to a PTE.
The idea here is that even though the computer knows its opponent's strategy and it could just play the Nashian best response to maximize its own utility, it should instead choose some potentially suboptimal strategy, such that a player who plays towards a PTE is better off than one who does not.
This way, it might be possible to train people into non-Nashian thinking.
To this end, we define a perfectly transparent best response, perfectly transparent $i$-best profile, and perfectly transparent $i$-optimal profile.
These three best responses induce three new equilibria: perfectly transparent best response equilibrium, perfectly transparent best profile Equilibrium, and perfectly transparent optimal profile equilibrium.
Furthermore, we analyze large datasets of randomly generated games to find out information about how they relate to each other, and to some other equilibria---most importantly to PTE.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                 %
%   KUIP  - Reference Manual -- LaTeX Source                      %
%                                                                 %
%   Chapter 6: KUIP Programming example                           %
%                                                                 %
%   External EPS files referenced: none                           %
%                                                                 %
%   Editor: Michel Goossens / CN-AS                               %
%   Last Mod.:  6 Dec  1991   mg                                  %
%                                                                 %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\chapter{KUIP Programming example}

As an example of how to implement a user interface with KUIP the code
for a simple Reverse Polish Notation pocket calculator is presented.
%
%---------------------------------------------------------------------------
%
\section{The Command Definition File}
\begin{XMPt}{The CDF for the RPN calculator}
>Name RPNDEF

>Menu RPN
>Guidance
Reverse Polish Notation sub-pocket calculator
using KUIP for the user interface.

>Command ENTER
>Parameters
+
NUM 'Enter a number' R D=0.
>Guidance
Push the number(s) given as parameter(s) into the stack.
If none push a zero.
>Action RPENT

>Command ADD
>Guidance
Push the number(s) given as parameter(s) into the stack.
Add the two upper-most numbers of the stack and shift it up.
>Action RPOPER

>Command SUBTRACT
>Guidance
Push the number(s) given as parameter(s) into the stack.
Subtract the two upper-most numbers of the stack and shift it up.
>Action RPOPER

>Command MULTIPLY
>Guidance
Push the number(s) given as parameter(s) into the stack.
Multiply the two upper-most numbers of the stack and shift it up.
>Action RPOPER
\condbreak{5\baselineskip}
>Command DIVIDE
>Guidance
Push the number(s) given as parameter(s) into the stack.
Divide the two upper-most numbers of the stack and shift it up.
>Action RPOPER

>Command PRINT
>Guidance
Print the content of the stack.
>Action RPRINT

>Command CLEAR
>Guidance
Clear the stack.
>Action RPCLR
\end{XMPt}
%
%---------------------------------------------------------------------------
%
\section{The application program}

\begin{XMPt}{FORTRAN code for the RPN calculator application program}
      PROGRAM RPN
      COMMON/PAWC/PAW(50000)
      COMMON/RPNSTK/ISTACK,STACK(100)
*---> Initialize ZEBRA and the store /PAWC/
      CALL MZEBRA(-3)
      CALL MZPAW(50000,' ')
*---> Initialize KUIP with 5000 words as minimum division size
      CALL KUINIT(5000)
*---> Create user command structure from definition file (command
*---> definition routine name RPNDEF defined in CDF
*---> with '>Name RPNDEF')
      CALL RPNDEF
*---> Change the prompt by executing the command SET/PROMPT
      CALL KUEXEC('SET/PROMPT ''RPN >''')
*---> Initialize the stack in /RPNSTK/
      CALL RPCLR
*---> Give control to KUIP (with no 'STYLE G', call KUWHAG
*---> to get 'STYLE G')
      CALL KUWHAT
*---> Typing 'QUIT' or 'EXIT' we return here
      END

      SUBROUTINE RPCLR
      COMMON/RPNSTK/ISTACK,STACK(100)
      DO 10 I=1,100
        STACK(I)=0.
10    CONTINUE
      ISTACK=50
      CALL RPRINT
      END

      SUBROUTINE RPENT
      COMMON/RPNSTK/ISTACK,STACK(100)
      CHARACTER*32 CMD
      CALL KUPATL(CMD,NPAR)
      IF(CMD.EQ.'ENTER' .AND. NPAR.EQ.0) NPAR=1
      DO 10 I=1,NPAR
        CALL KUGETR(R)
        ISTACK=ISTACK+1
        IF(ISTACK.GT.100) THEN
          PRINT *,'ERROR: Stack overflow'
          GOTO 20
        ENDIF
        STACK(ISTACK)=R
10    CONTINUE
20    CONTINUE
      IF(CMD.EQ.'ENTER') CALL RPRINT
      END

      SUBROUTINE RPOPER
      COMMON/RPNSTK/ISTACK,STACK(100)
      CHARACTER*32 CMD
      CALL KUPATL(CMD,NPAR)
      CALL RPENT
      IF(ISTACK.LT.2) THEN
        PRINT *,'ERROR: Stack underflow'
        GOTO 10
      ENDIF
      IF(CMD.EQ.'ADD') THEN
        STACK(ISTACK-1)=STACK(ISTACK)+STACK(ISTACK-1)
      ELSEIF(CMD.EQ.'SUBTRACT') THEN
        STACK(ISTACK-1)=STACK(ISTACK)-STACK(ISTACK-1)
      ELSEIF(CMD.EQ.'MULTIPLY') THEN
        STACK(ISTACK-1)=STACK(ISTACK)*STACK(ISTACK-1)
      ELSEIF(CMD.EQ.'DIVIDE') THEN
        IF(STACK(ISTACK).EQ.0) THEN
          PRINT *,'ERROR: Divide by zero'
        ELSE
          STACK(ISTACK-1)=STACK(ISTACK-1)/STACK(ISTACK)
        ENDIF
      ENDIF
      ISTACK=ISTACK-1
10    CONTINUE
      CALL RPRINT
      END

      SUBROUTINE RPRINT
      COMMON/RPNSTK/ISTACK,STACK(100)
      PRINT *,(STACK(I),I=ISTACK,ISTACK-3,-1)
      IF(STACK(ISTACK).GT.0) THEN
        PRINT *,'********'
      ELSE\condbreak{4\baselineskip}
        PRINT *,'*********'
      ENDIF
      END
\end{XMPt}
\Rind[KUPATL]{}
\Rind[KUGETR]{}
%
%---------------------------------------------------------------------------
%
%\newpage
\section{Example of a run}

\begin{XMPt}{Example of a session using the RPN calculator}
$ rpn
 0.0000000 0.0000000 0.0000000 0.0000000
 *********
 RPN > \underline{style an}

 From  /...

  1:   KUIP          Command Processor commands.
  2:   MACRO         Macro Processor commands.
  3:   VECTOR        Vector Processor commands.
  4:   RPN           Reverse Polish Notation sub-pocket calculator

 Enter a number ('Q'=command mode): \underline{4}

 From  /RPN/...

  1: * ENTER         Push the number(s) given as parameter(s) into the stack.
  2: * ADD           Push the number(s) given as parameter(s) into the stack.
  3: * SUBTRACT      Push the number(s) given as parameter(s) into the stack.
  4: * MULTIPLY      Push the number(s) given as parameter(s) into the stack.
  5: * DIVIDE        Push the number(s) given as parameter(s) into the stack.
  6: * PRINT         Print the content of the stack.
  7: * CLEAR         Clear the stack.

 Enter a number ('\'=one level back, 'Q'=command mode): \underline{1}

  * /RPN/ENTER  [ NUM ]

 Add parameters or just <CR> to the command line
 ('#'=cancel execution, '?'=help) :

 /RPN/ENTER \underline{1 2 3}
 3.000000 2.000000 1.000000 0.0000000
 ********

 From  /RPN/...

  1: * ENTER         Push the number(s) given as parameter(s) into the stack.
  2: * ADD           Push the number(s) given as parameter(s) into the stack.
  3: * SUBTRACT      Push the number(s) given as parameter(s) into the stack.
  4: * MULTIPLY      Push the number(s) given as parameter(s) into the stack.
  5: * DIVIDE        Push the number(s) given as parameter(s) into the stack.
  6: * PRINT         Print the content of the stack.
  7: * CLEAR         Clear the stack.

 Enter a number ('\'=one level back, 'Q'=command mode): \underline{2}

  * /RPN/ADD

 Add parameters or just <CR> to the command line
 ('#'=cancel execution, '?'=help) :

 /RPN/ADD
 5.000000 1.000000 0.0000000 0.0000000
 ********
 From  /RPN/...

  1: * ENTER         Push the number(s) given as parameter(s) into the stack.
  2: * ADD           Push the number(s) given as parameter(s) into the stack.
  3: * SUBTRACT      Push the number(s) given as parameter(s) into the stack.
  4: * MULTIPLY      Push the number(s) given as parameter(s) into the stack.
  5: * DIVIDE        Push the number(s) given as parameter(s) into the stack.
  6: * PRINT         Print the content of the stack.
  7: * CLEAR         Clear the stack.

 Enter a number ('\'=one level back, 'Q'=command mode): \underline{q}\vspace{7pt}
 RPN > \underline{print}
 5.000000 1.000000 0.0000000 0.0000000
 ********
 RPN > \underline{enter 1 2 3}
 3.000000 2.000000 1.000000 5.000000
 ********
 RPN > \underline{add}
 5.000000 1.000000 5.000000 1.000000
 ********
 RPN > \underline{add}
 6.000000 5.000000 1.000000 0.0000000
 ********
 RPN > \underline{add}
 11.00000 1.000000 0.0000000 0.0000000
 ********
 RPN > \underline{add 5}
 16.00000 1.000000 0.0000000 0.0000000
 ********
 RPN > \underline{add 10 20}
 30.00000 16.00000 1.000000 0.0000000
 ********
 RPN > \underline{usage rpn/}

  * RPN/ENTER  [ NUM ]
  * RPN/ADD
  * RPN/SUBTRACT
  * RPN/MULTIPLY
  * RPN/DIVIDE
  * RPN/PRINT
  * RPN/CLEAR

 RPN > \underline{help clear}

  * /RPN/CLEAR

    Clear the stack.

 RPN > \underline{c}
 *** Ambiguous command. Possible commands are :

 /KUIP/ALIAS/CREATE
 /KUIP/SET_SHOW/COMMAND
 /KUIP/SET_SHOW/COLUMNS
 /MACRO/SYNTAX/Branching/CASE
 /VECTOR/CREATE
 /VECTOR/COPY
 /RPN/CLEAR

 RPN > \underline{r/c}
 0.0000000 0.0000000 0.0000000 0.0000000
 *********
 RPN > \underline{help rpn/print}

  * /RPN/PRINT

    Print the content of the stack.

 RPN > \underline{help en}

  * /RPN/ENTER  [ NUM ]

    NUM        R 'Enter a number' D=0

    Push the number(s) given as parameter(s) into the stack.
    If none push a zero.

 RPN > \underline{q}
$
\end{XMPt}


\section{Axioms for propositional logic}


\lab{NumPy Visual Guide}{NumPy Visual Guide}
\label{appendix:numpy-visual-guide}
\objective{NumPy operations can be difficult to visualize, but the concepts are straightforward.
This appendix provides visual demonstrations of how NumPy arrays are used with slicing syntax, stacking, broadcasting, and axis-specific operations.
Though these visualizations are for 1- or 2-dimensional arrays, the concepts can be extended to $n$-dimensional arrays.
% See Lab \ref{lab:NumPy} for an introduction to NumPy operations and synatx.
}

\section*{Data Access} % ======================================================

The entries of a 2-D array are the rows of the matrix (as 1-D arrays).
To access a single entry, enter the row index, a comma, and the column index.
Remember that indexing begins with $0$.

\begin{align*}
\text{\li{A[0]}} = \left[\begin{array}{rrrrr}
\rowcolor{red!20}
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\end{array}\right]
&&
\text{\li{A[2,1]}} = \left[\begin{array}{rrrrr}
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \cellcolor{red!20} \times & \times & \times & \times\\
\times & \times & \times & \times & \times
\end{array}\right]
\end{align*}

\section*{Slicing} % ==========================================================

A lone colon extracts an entire row or column from a 2-D array.
The syntax \li{[a:b]} can be read as ``the $a$th entry up to (but not including) the $b$th entry.''
Similarly, \li{[a:]} means ``the $a$th entry to the end'' and \li{[:b]} means ``everything up to (but not including) the $b$th entry.''

\begin{align*}
\text{\li{A[1]}} = \text{\li{A[1,:]}} = \left[\begin{array}{rrrrr}
\times & \times & \times & \times & \times\\
\rowcolor{red!20}
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\end{array}\right]
&&
\text{\li{A[:,2]}} = \left[\begin{array}{rr>{\columncolor{red!20}}rrr}
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\end{array}\right]
\end{align*}

\begin{align*}
\text{\li{A[1:,:2]}} = \left[\begin{array}{>{\columncolor{red!20}}r>{\columncolor{red!20}}rrrr}
\cellcolor{white}\times & \cellcolor{white}\times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times\\
\times & \times & \times & \times & \times
\end{array}\right]
&&
\text{\li{A[1:-1,1:-1]}} = \left[\begin{array}{rrrrr}
\times & \times & \times & \times & \times\\
\rowcolor{red!20}
\cellcolor{white}\times & \times & \times & \times & \cellcolor{white}\times\\
\rowcolor{red!20}
\cellcolor{white}\times & \times & \times & \times & \cellcolor{white}\times\\
\times & \times & \times & \times & \times\end{array}\right]
\end{align*}

\section*{Stacking} % =========================================================

\li{np.hstack()} stacks sequence of arrays horizontally and \li{np.vstack()} stacks a sequence of arrays vertically.

\begin{align*}
\text{\li{A}} = \left[\begin{array}{ccc}
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}
\end{array}\right]
&&
\text{\li{B}} = \left[\begin{array}{ccc}
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*} \\
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*} \\
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*}
\end{array}\right]
\end{align*}

\begin{align*}
\text{\li{np.hstack((A,B,A))}} =
\left[\begin{array}{ccccccccc}
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*}&
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*}&
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*}&
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}
\end{array}\right]
\end{align*}

\begin{align*}
\text{\li{np.vstack((A,B,A))}} =
\left[\begin{array}{ccc}
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*} \\
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*} \\
\textcolor{red}{*} & \textcolor{red}{*} & \textcolor{red}{*} \\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}
\end{array}\right]
\end{align*}
Because 1-D arrays are flat, \li{np.hstack()} concatenates 1-D arrays and \li{np.vstack()} stacks them vertically.
To make several 1-D arrays into the columns of a 2-D array, use \li{np.column_stack()}.

\begin{align*}
\text{\li{x}} = \left[\begin{array}{cccc}
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}
\end{array}\right]
&&
\text{\li{y}} = \left[\begin{array}{cccc}
\textcolor{red}{*}&\textcolor{red}{*}&\textcolor{red}{*}&\textcolor{red}{*}\end{array}\right]
\end{align*}

\begin{align*}
\text{\li{np.hstack((x,y,x))}} =
\left[\begin{array}{cccccccccccc}
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&
\textcolor{red}{*}&\textcolor{red}{*}&\textcolor{red}{*}&\textcolor{red}{*}&
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}
\end{array}\right]
\end{align*}

\begin{align*}
\text{\li{np.vstack((x,y,x))}} =
\left[\begin{array}{cccc}
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}\\
\textcolor{red}{*}&\textcolor{red}{*}&\textcolor{red}{*}&\textcolor{red}{*}\\
\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}&\textcolor{blue}{\times}
\end{array}\right]
&&
\text{\li{np.column_stack((x,y,x))}} =
\left[\begin{array}{ccc}
\textcolor{blue}{\times}&\textcolor{red}{*}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{red}{*}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{red}{*}&\textcolor{blue}{\times}\\
\textcolor{blue}{\times}&\textcolor{red}{*}&\textcolor{blue}{\times}
\end{array}\right]
\end{align*}
The functions \li{np.concatenate()} and \li{np.stack()} are more general versions of \li{np.hstack()} and \li{np.vstack()}, and \li{np.row_stack()} is an alias for \li{np.vstack()}.

\section*{Broadcasting} % =====================================================

NumPy automatically aligns arrays for component-wise operations whenever possible.
% The default behavior adds the first element to the first element of each row, the second element to the second element of each row, and so on.
See \url{http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html} for more in-depth examples and broadcasting rules.

\begin{align*}
\text{\li{A}} = \left[\begin{array}{ccc}
1 & 2 & 3\\
1 & 2 & 3\\
1 & 2 & 3\\
\end{array}\right]
&&
\text{\li{x}} = \left[\begin{array}{ccc}
10 & 20 & 30
\end{array}\right]
\end{align*}

\begin{align*}
\text{\li{A + x}}
&= \begin{blockarray}{ccc}
\begin{block}{[ccc]}
1 & 2 & 3\\
1 & 2 & 3\\
1 & 2 & 3\\
\end{block}
  & + &  \\
\begin{block}{[ccc]}
10 & 20 & 30\\
\end{block}
\end{blockarray}
&= \left[\begin{array}{ccc}
11 & 22 & 33\\
11 & 22 & 33\\
11 & 22 & 33
\end{array}\right]
\\ \\
\text{\li{A + x.reshape((-1,1))}}
&= \left[\begin{array}{ccc}
1 & 2 & 3 \\
1 & 2 & 3 \\
1 & 2 & 3 \\
\end{array}\right]
+ \left[\begin{array}{c}
10 \\ 20 \\ 30\\
\end{array}\right]
&= \left[\begin{array}{ccc}
11 & 12 & 13\\
21 & 22 & 23\\
31 & 32 & 33\\
\end{array}\right]
\end{align*}

\section*{Operations along an Axis} % =========================================

Most array methods have an \li{axis} argument that allows an operation to be done along a given axis.
To compute the sum of each column, use \li{axis=0}; to compute the sum of each row, use \li{axis=1}.

\begin{align*}
A = \left[\begin{array}{cccc}
1 & 2 & 3 & 4\\
1 & 2 & 3 & 4\\
1 & 2 & 3 & 4\\
1 & 2 & 3 & 4
\end{array}\right]
\end{align*}

\begin{align*}
\text{\li{A.<<sum>>(axis=0)}} &= %\text{\li{np.array([sum(A[:,j]) for j in range(A.shape[1])])}} =
\left[\begin{array}{>{\columncolor{red!20}}c|>{\columncolor{blue!20}}c|>{\columncolor{yellow!20}}c|>{\columncolor{green!20}}c}
1 & 2 & 3 & 4\\
1 & 2 & 3 & 4\\
1 & 2 & 3 & 4\\
1 & 2 & 3 & 4
\end{array}\right]
= \left[\begin{array}{cccc} \cellcolor{red!20}4 & \cellcolor{blue!20}8 & \cellcolor{yellow!20}12 & \cellcolor{green!20}16 \end{array}\right]
\\ \\
\text{\li{A.<<sum>>(axis=1)}} &= %\text{\li{np.array([sum(A[i,:]) for i in range(A.shape[0])])}} =
\left[\begin{array}{cccc}
\rowcolor{red!20} 1 & 2 & 3 & 4\\ \hline
\rowcolor{blue!20} 1 & 2 & 3 & 4\\ \hline
\rowcolor{yellow!20} 1 & 2 & 3 & 4\\ \hline
\rowcolor{green!20} 1 & 2 & 3 & 4\\
\end{array}\right]
= \left[\begin{array}{cccc} \cellcolor{red!20}10 & \cellcolor{blue!20}10 & \cellcolor{yellow!20}10 & \cellcolor{green!20}10 \end{array}\right]
\end{align*}


 \documentclass [12pt]{article}

\usepackage {amsmath}
\usepackage {amsthm}
\usepackage {amssymb}
\usepackage {graphicx}
\usepackage {float}
\usepackage {multirow}
\usepackage {xcolor}
\usepackage {algorithmic}
\usepackage [ruled,vlined,commentsnumbered,titlenotnumbered]{algorithm2e} \usepackage {array}
\usepackage {booktabs}
\usepackage {url}
\usepackage {parskip}
\usepackage [margin=1in]{geometry}
\usepackage [T1]{fontenc}
\usepackage {cmbright}
\usepackage [many]{tcolorbox}
\usepackage [colorlinks = true,
            linkcolor = blue,
            urlcolor  = blue,
            citecolor = blue,
            anchorcolor = blue]{hyperref}
\usepackage {enumitem}
\usepackage {xparse}
\usepackage {verbatim}
\usepackage{listings}
\usepackage{xcolor}
\lstset { %
    language=C++,
    backgroundcolor=\color{black!5}, % set backgroundcolor
    basicstyle=\footnotesize,% basic font setting
}
\newtheorem{theorem}{Theorem}
\newtheorem{remark}{Remark}

\DeclareTColorBox {Solution}{}{breakable, title={Solution}} \DeclareTColorBox {Solution*}{}{breakable, title={Solution (provided)}} \DeclareTColorBox {Instruction}{}{boxrule=0pt, boxsep=0pt, left=0.5em, right=0.5em, top=0.5em, bottom=0.5em, arc=0pt, toprule=1pt, bottomrule=1pt} \DeclareDocumentCommand {\Expecting }{+m}{\textbf {[We are expecting:} #1\textbf {]}} \DeclareDocumentCommand {\Points }{m}{\textbf {(#1 pt.)}}

\begin {document}

{\LARGE \textbf {COMP 285 (NC A\&T, Spr `22)}\hfill \textbf {Lecture 7} }
\vspace {1em}
\begin {Instruction}

Adapted From Virginia Williams' lecture notes. Additional credits: J. Su, W. Yang, Gregory Valiant, Mary Wootters, Aviad Rubinstein, Sami Alsheikh.
\end {Instruction}

\begin{centering}
\section*{k-Select Problem \& Median Selection}
\end{centering}

\section{Introduction}
In the last lecture, we wrapped up with the subtitution method for solving recurrences and introduced the selection problem. In this lecture, we find an $O(n)$ algorithm to solve the selection problem.

\section{Selection Problem}
The selection problem is to find the $k$-th smallest number in an array $A$.

\textbf{Input}: array $A$ of $n$ numbers, and an integer $k \in \{1, \cdot , n\}$.
\textbf{Output}: the $k$-th smallest number in $A$.

One approach is to sort the numbers in ascending order, and then return the $k$th number in the sorted list. This takes $O(n \log n)$ time, since it takes $O(n \log n)$ time for the sort (e.g. by \texttt{MergeSort}) and $O(1)$ time to return $k$th number.

\subsection{Minimum Element}
As always, we ask if we can do better (i.e. faster in big-O terms). In the special case where $k = 1$, selection is the problem of finding the minimum element. We can do this in $O(n)$ time by scanning through the array and keeping track of the minimum element so far. If the current element is smaller than the minimum so far, we update the minimum.

\begin{algorithm}
\caption{SelectMin(A)}\label{alg:min}
\begin{algorithmic}
\STATE $m \gets \infty$
\STATE $n \gets $ length($A$)
\FOR{$i=1$ to $n$}
    \IF{$A[i] < m$}
        \STATE $m \gets A[i]$
    \ENDIF
\ENDFOR
\end{algorithmic}
\end{algorithm}

In fact, this is the best running time we could hope for.

\textbf{Definition.} A deterministic algorithm is one which, given a fixed input, always performs the same operations (as opposed to an algorithm which uses randomness).

\textbf{Claim.} \textit{Any deterministic algorithm for finding the minimum has runtime $\Omega(n)$.}

\begin{proof}
Intuitively, the claim holds because any algorithm for the minimum must
look at all the elements, each of which could be the minimum. Suppose a correct deterministic algorithm does not look at $A[i]$ for some $i$. Then the output cannot depend on $A[i]$, so the algorithm returns the same value whether $A[i]$ is the minimum element or the maximum element. Therefore the algorithm is not always correct, which is a contradiction. So there is no sublinear deterministic algorithm for finding the minimum.
\end{proof}

So for $k = 1$, we have an algorithm which achieves the best running time possible. By similar reasoning, this lower bound of $\Omega(n)$ applies to the general selection problem. So ideally we would like to have a linear-time selection algorithm in the general case.

\section{Linear-Time Selection}
In fact, a linear-time selection algorithm does exist. Before showing the linear time selection algorithm, it's helpful to build some intuition on how to approach the problem. The high-level idea will be to try to do a Binary Search over an unsorted input. At each step, we hope to divide the input into two parts, the subset of smaller elements of $A$, and the subset of larger elements of $A$. We will then determine whether the $k$-th smallest element lies in the first part (with the ``smaller'' elements) or the part with larger elements, and recurse on exactly one of
those two parts.

How do we decide how to partition the array into these two pieces? Suppose we have a
black-box algorithm \texttt{ChoosePivot} that chooses some element in the array $A$, and we use this pivot to define the two sets–any $A[i]$ less than the pivot is in the set of ``smaller'' values, and any $A[i]$ greater than the pivot is in the other part. We will figure out precisely how to specify this subroutine \texttt{ChoosePivot} a bit later, after specifying the high-level algorithm structure. The algorithm \texttt{ChoosePivot} does not affect the \textit{correctness} of the algorithm as we will see in \ref{alg:select}. Rather, it only affects the runtime.

For clarity we'll assume all elements are distinct from now on, but the idea generalizes easily. Let $n$ be the size of the array and assume we are trying to find the $k$-th element.

\begin{algorithm}
\caption{Select(A, n, k)}\label{alg:select}
\begin{algorithmic}
\IF {$n=1$}
    \RETURN $A[1]$
\ENDIF
\STATE $p \gets \texttt{ChoosePivot}(A, n)$
\STATE $A_< \gets \{A[i] \mid A[i] < p \}$
\STATE $A_> \gets \{A[i] \mid A[i] > p \}$
\IF {$|A_<| = k - 1$}
    \RETURN $p$
\ELSIF {$|A_<| > k - 1$}
    \RETURN \texttt{Select}$(A_<, |A_<|, k)$
\ELSIF {$|A_<| < k - 1$}
    \RETURN \texttt{Select}$(A_>, |A_>|, k - |A_<| - 1)$
\ENDIF
\end{algorithmic}
\end{algorithm}

At each iteration, we use the element $p$ to partition the array into two parts: all elements smaller than the pivot and all elements larger than the pivot, which we denote $A_<$ and $A_>$, respectively.

Depending on what the size of the resulting sub-arrays are, the runtime can be different. For example, if one of these sub-arrays is of size $n - 1$, at each iteration, we only decreased the size of the problem by 1, resulting in total running time $O(n^2)$. If the array is split into two equal parts, then the size of the problem at iteration reduces by half, resulting in a linear time solution. (We assume \texttt{ChoosePivot} runs in $O(n)$.)

\textbf{Proposition.} \textit{ If the pivot p is chosen to be the minimum or maximum element, then Select runs in $\Theta(n^2)$ time.}

\textit{Proof.} At each iteration, the number of elements decreases by $1$. Since running \texttt{ChoosePivot} and creating $A_<$ and $A_>$ takes linear time, the recurrence for the runtime is $T(n) = T(n - 1) + \Theta(n)$. Expanding this,
$$
T(n) \leq c_1n + c_1(n -  1) + c_1(n -  2) + ... + c_1 = c_1n(n + 1)/2
$$
and
$$
T(n) \geq c_2n + c_2(n -  1) + c_2(n -  2) + ... + c_2 = c_2n(n + 1)/2.
$$
We conclude that $T(n) = \Theta(n^2)$.

\textbf{Proposition}. \textit{If the pivot $p$ is chosen to be the median element, then Select runs in $O(n)$ time.}

\textit{Proof}. Intuitively, the running time is linear since we remove half of the elements from consideration each iteration. Formally, each recursive call is made on inputs of half the size, namely, $T(n) \leq T(n/2)+cn$. Expanding this, the runtime is $T(n) \leq cn+cn/2+cn/4+...+c \leq 2cn$, which is $O(n)$. So how do we design \texttt{ChoosePivot} that chooses a pivot in linear time? In the following, we describe three ideas.

\subsection{Idea \#1: Choose a random pivot}

As we saw earlier, depending on the pivot chosen, the worst-case runtime can be $O(n^2)$ if we are unlucky in the choice of the pivot at every iteration. As you might expect, it is extremely unlikely to be this unlucky, and one can prove that the expected runtime is $O(n)$ provided the pivot is chosen uniformly at random from the set of elements of $A$. In practice, this randomized algorithm is what is implemented, and the hidden constant in the $O(n)$ runtime is very small.

\subsection{Idea \#2: Choose a pivot that creates the most ``balanced'' split}

Consider \texttt{ChoosePivot} that returns the pivot that creates the most ``balanced'' split, which would be the median of the array. However, this is exactly selection problem we are trying to solve, with $k = n/2$! As long as we do not know how to find the median in linear time, we cannot use this procedure as \texttt{ChoosePivot}.

\end{document}


\chapter{Efficient Retrieval}

\begin{multicols*}{2}
\section{Dimension Reduction}

\noindent To reduce sparsity problem, we use dimensionality reduction technique, such as truncated singular value decomposition, to discard some small / unimportant singular values.

\section{Efficient Cosine Ranking}
\noindent To compute $K$ highest-scoring document for a query, we need to compute cosine similarity for all documents. To improve efficiency, we can consider only $K$ documents that likely to be among $K$ highest scoring documents.

\subsection{Index Elimination}
\noindent Rule 1: only consider documents that contain at least one query term \\
\noindent Rule 2: only consider high-IDF query terms. Low-IDF terms are likely to be stopwords and contribute little to the scores. \\
\noindent Rule 3: only consider documents that contain many query terms. This can be done during posting traversal.

\subsection{Champion Lists}
\noindent Precompute, for each term $t$ in the dictionary, the set of the $r$ documents with the highest weights for $t$; the value of $r$ is chosen in advance. We call this set of $r$ documents the champion list for term $t$.\\

\noindent At query time, we only compute scores for documents in the champion list for some query terms.

\subsection{Static Quality Scores}
\noindent We want top-ranking documents to be both relevant and authoritative. Let the query-independent quality score as $g(d)$, the net score:
$$\text{netscore}(\vec{q},\vec{d})=g(d) + \text{cosine} (\vec{q},\vec{d})$$

\noindent We get the top $K$ documents by net score

\subsection{High and Low Lists}
\noindent We maintain two posting lists called high and low. We first traverse high lists first to get top $K$ documents. If we do not get enough document, then only we traverse low lists.

\subsection{Cluster Pruning}
\noindent We first pick $\sqrt{N}$ documents at random (leaders), then pre-compute the nearest leader for all documents. \\
\noindent We process query by finding its nearest leader $L$ and find $K$ nearest document from $L$’s followers

\section{Parametric and Zone Indexes}
\noindent There could be some metadata / fields for each documents. We want to search by fields. \\

\noindent A zone is a region of documents that can contain an arbitrary amount of text. We build inverted indexes on zones to permit querying.

\begin{center}
\includegraphics[width=8cm]{zone-index}
\end{center}

\section{Tiered Indexes}
\noindent Break postings up into a hierarchy of lists. Inverted index thus broken up into tiers of decreasing importance.\\

\noindent We process query by using top tier unless it fails to yield $K$ documents.

\section{Aggregate Scores}

$$\text{score} = \alpha \times \text{cosine}(\vec{q},\vec{d}) + \beta \times g(d) + \gamma \times \text{proximity}(\vec{q},\vec{d})$$

\end{multicols*}