\subsection{Modules}\label{subsec:modules}

\begin{definition}\label{def:left_module}
  Let \( R \) be a \hyperref[def:semiring/dioid]{dioid} and \( M \) be an abelian group. Analogously to \fullref{def:left_monoid_action}, we say that \( M \) is a left \( R \)-\term{module} if it has some additional structure, which can be defined equivalently as
  \begin{thmenum}
    \thmitem{def:left_module/homomorphism} a dioid homomorphism from \( R \) to the endomorphism \hyperref[def:endomorphism_dioid]{ring} \( \End(M) \)

    \thmitem{def:left_module/multiplication}\cite[374]{Knapp2016BasicAlgebra} an \hyperref[def:magma/associative]{associative} and \hyperref[def:unital_magma]{unital} \hyperref[def:left_monoid_action/operation]{operation} \( \cdot: R \times M \to M \), written using juxtaposition.

    We require that \( \cdot \) is associative, distributive over \( + \), and compatible with the identity in \( R \). Explicitly, the following are satisfied for \( x, y \in M \) and \( s, t \in R \):
    \begin{thmenum}
      \thmitem{def:left_module/associativity}(associativity) \( s \cdot (t \cdot x) = (s t) \cdot x \).
      \thmitem{def:left_module/scalar_distributivity}(scalar distributivity) \( (s + t) \cdot x = s \cdot x + t \cdot x \).
      \thmitem{def:left_module/vector_distributivity}(vector distributivity) \( t \cdot (x + y) = t \cdot x + t \cdot y \).
      \thmitem{def:left_module/identity}(identity) \( 1_R \cdot x = x \).
    \end{thmenum}
  \end{thmenum}

  In analogy with \hyperref[def:vector_space]{vector spaces}, we call elements of \( R \) scalars and elements of \( M \) vectors. See \fullref{def:vector_space}.

  We denote the category of modules over \( R \) by \( \cat{Mod}_R \).
\end{definition}
\begin{proof}
  \ImplicationSubProof{def:left_module/homomorphism}{def:left_module/multiplication} Let \( \tau: R \to \End(M) \) be a ring homomorphism. Define the operation
  \begin{balign*}
     & \cdot: R \times M \to M           \\
     & \cdot(r, m) \coloneqq \tau(r)(m).
  \end{balign*}

  Let \( x, y \in M \) and \( s, t \in R \). From the fact that \( \tau \) is a ring homomorphism, we have
  \SubProofOf{def:left_module/associativity}
  \begin{equation*}
    s \cdot (t \cdot x)
    =
    \tau(s)(\tau(t)(x))
    =
    (\tau(s) \circ \tau(t))(x)
    =
    \tau(st)(x).
  \end{equation*}

  \SubProofOf{def:left_module/scalar_distributivity}
  \begin{equation*}
    (s + t) \cdot x
    =
    \tau(s + t)(x)
    =
    \tau(s)(x) + \tau(t)(x)
    =
    s \cdot x + t \cdot x.
  \end{equation*}

  \SubProofOf{def:left_module/vector_distributivity}
  \begin{equation*}
    t \cdot (x + y)
    =
    \tau(t)(x + y).
  \end{equation*}

  Now since \( \tau(t) \) is a group endomorphism, we have \( \tau(t)(x + y) = \tau(t)(x) + \tau(t)(y) \). Thus,
  \begin{equation*}
    t \cdot (x + y)
    =
    t \cdot x + t \cdot y.
  \end{equation*}

  \SubProofOf{def:left_module/identity}
  \begin{equation*}
    1_R \cdot x
    =
    \tau(1_R)(x)
    =
    \id(x)
    =
    x.
  \end{equation*}

  \ImplicationSubProof{def:left_module/multiplication}{def:left_module/homomorphism} Let \( \cdot: R \times M \to M \) be a left scalar multiplication operation. We define
  \begin{balign*}
     & \tau: R \to \End(M)                      \\
     & \tau(t) \coloneqq (x \mapsto t \cdot x).
  \end{balign*}

  This function is well-defined since for each \( t \in R \), the function \( \tau(t) \) is an abelian group homomorphism (due to \fullref{def:left_module/associativity}).

  \( \tau \) is a ring homomorphism because
  \begin{itemize}
    \item it preserves addition:
          \begin{equation*}
            \tau(s + t)
            =
            (x \mapsto (s + t) \cdot x)
            =
            (x \mapsto s \cdot x + t \cdot x)
            =
            \tau(s) + \tau(t).
          \end{equation*}

    \item it preserves multiplication:
          \begin{equation*}
            \tau(st)
            =
            (x \mapsto (st) \cdot x)
            =
            (x \mapsto (s \cdot (t \cdot x)))
            =
            \tau(s) \circ \tau(t).
          \end{equation*}

    \item it preserves identities:
          \begin{equation*}
            \tau(1_R)
            =
            \id,
          \end{equation*}
          which is the multiplicative unit in \( \End(M) \).
  \end{itemize}
\end{proof}

\begin{definition}\label{def:right_module}
  We say that \( \tau: R \to \End(A) \) is a \term{right \( R \)-module} if the same function is a \hyperref[def:left_module]{left module} on the opposite \hyperref[def:opposite_ring]{ring} \( R^{-1} \).
\end{definition}

\begin{definition}\label{def:bimodule}
  An abelian group \( M \) that is both a left \( L \)-module and right \( R \)-module is called an \( L, R \)-\term{bimodule} if \( l \in L, r \in R \) and \( x \in M \) implies
  \begin{equation*}
    (lx)r = l(xr).
  \end{equation*}
\end{definition}

\begin{proposition}\label{thm:def:left_module/properties}
  Any left \( R \)-module \( M \) has the following basic properties:
  \begin{thmenum}
    \thmitem{thm:def:left_module/properties/ring_zero_is_absorbing} Multiplication by \( 0_R \) is \term{absorbing}, that is, \( 0_R x = 0_M \) for any \( x \in M \).

    \thmitem{thm:def:left_module/properties/module_zero_is_absorbing} Multiplication by \( 0_M \) is absorbing, that is, \( t 0_M = 0_M \) for any \( t \in R \).
  \end{thmenum}
\end{proposition}
\begin{proof}
  \SubProofOf{thm:def:left_module/properties/ring_zero_is_absorbing} For any \( x \in M \) we have that \( 0_R x = (0_R + 0_R)x = 0_R x + 0_R x \), thus \( 0_R x \) is an additive identity and \( 0_R x = 0_M \).

  \SubProofOf{thm:def:left_module/properties/module_zero_is_absorbing} For any \( t \in R \) we have that \( t 0_M = t (0_M + 0_M) = t 0_M + t 0_M \), thus \( t 0_M \) is an additive identity and \( t 0_M = 0_M \).
\end{proof}

\begin{example}\label{ex:module/ideal_of_ring}
  Every unital ring \( R \) is a module over itself. Every ideal \( I \subseteq R \) is an \( R \)-module since it is closed under multiplication with \enquote{scalars} from \( R \).
\end{example}

\begin{definition}\label{def:left_module_kernel}
  The \term{kernel} \( \ker(f) \) of a left \( R \)-module homomorphism \( f: M \to N \) is the \hyperref[def:zero_locus]{zero locus} of \( f \), that is, \hyperref[thm:def:function/properties/preimage]{preimage} \( f^{-1}(0_N) \).

  It is an instance of \fullref{def:zero_morphisms/kernel}.
\end{definition}

\begin{definition}\label{def:quotient_left_module}
  Let \( M \) be a left module and \( N \) be a submodule of \( M \). Define the \term{quotient module}
  \begin{equation*}
    M / N \coloneqq \{ x + N \colon x \in M \}
  \end{equation*}
  with the operations
  \begin{balign*}
    x + N \oplus y + N \coloneqq x + y + N.
    t \odot x + N \coloneqq tx + N.
  \end{balign*}

  Define the canonical projection homomorphism
  \begin{balign*}
     & \pi: G \to G / N        \\
     & \pi(x) \coloneqq x + N.
  \end{balign*}

  The kernel of \( \pi \) is precisely \( N \).
\end{definition}
\begin{proof}
  The proof of correctness is similar to \fullref{def:quotient_group}.
\end{proof}

\begin{theorem}\label{thm:homomorphism_theorem_for_left_modules}
  Fix a ring \( R \). Let \( \varphi: M \to K \) be a homomorphism of left \( R \)-modules. We have the isomorphism
  \begin{equation*}
    M / \ker \varphi \cong \img \varphi.
  \end{equation*}
\end{theorem}
\begin{proof}
  Analogous to \fullref{thm:homomorphism_theorem_for_groups}.
\end{proof}

\begin{definition}\label{def:linear_operator}
  Let \( M \) and \( N \) be two left \( R \)-modules. We say that the function \( f: M \to N \) is \term{linear} or a \term{linear operator} if it satisfies the conditions
  \begin{thmenum}
    \thmitem{def:linear_operator/additivity}(additivity) \( f(x + y) = f(x) + f(y) \) for any \( x, y \in M \).
    \thmitem{def:linear_operator/homogeneity}(homogeneity) \( f(tx) = t f(x) \) for any \( t \in R \) and \( x \in M \) (see \fullref{def:homogenous_function}).
  \end{thmenum}
\end{definition}

\begin{proposition}\label{thm:map_is_linear_iff_homomorphism}
  A function \( f: M \to N \) between left \( R \)-modules is \hyperref[def:linear_operator]{linear} if and only if it is a module homomorphism in the sense of \fullref{def:first_order_homomorphism}.
\end{proposition}
\begin{proof}
  Since \( (M, +) \) and \( (N, +) \) are groups, it follows from \fullref{thm:group_homomorphism_single_condition} that \fullref{def:linear_operator/additivity} is equivalent to the requirement that \( f \) is a homomorphism between \( (M, +) \) and \( (N, +) \).

  Now fix \( t \in R \). For \( f \) to be a homomorphism, it must satisfy
  \begin{equation*}
    f(\cdot_M^{(t)}(x)) = \cdot_t^{(N)}(f(x)),
  \end{equation*}
  which is just a more formal way to write \fullref{def:linear_operator/homogeneity}.
\end{proof}

\begin{definition}\label{def:affine_operator}
  Let \( M \) and \( N \) be two left \( R \)-modules. We say that the function \( f: M \to N \) is \term{affine} if it is a translation of a \hyperref[def:linear_operator]{linear function}, that is, if there exists a linear function \( l: M \to N \) and a constant \( a \in N \) such that \( f(x) = l(x) + a \).
\end{definition}

\begin{definition}\label{def:multilinear_function}
  Generalizing \fullref{def:linear_operator}, if \( M_1, \ldots, M_k \) and \( N \) are \( R \)-modules, we say that the function
  \begin{equation*}
    f: M_1 \times \ldots \times M_k \to N
  \end{equation*}
  is \term{multilinear} or \term{\( k \)-linear} (\term{bilinear} for \( k = 2 \), \term{trilinear} for \( k = 3 \)) if it is linear in each component, that is, for each component \( i = 1, \ldots, k \), and for each tuple not containing elements from \( M_i \),
  \begin{equation*}
    (u_1, \ldots, u_{i-1}, u_{i+1}, \ldots, u_k) \in M_1 \times \ldots \times M_{i-1} \times M_{i+1} \times \ldots \times M_k \to N
  \end{equation*}
  the following function is linear:
  \begin{balign*}
     & f_i: M_i \to N                                                         \\
     & f_i(u_i) \coloneqq f(u_1, \ldots, u_{i-1}, u_i, u_{i+1}, \ldots, u_k).
  \end{balign*}
\end{definition}

\begin{definition}\label{def:abelian_group_z_module}\mcite[375]{Knapp2016BasicAlgebra}
  Let \( G \) be an abelian group. Associate with \( G \) the \( \BbbZ \)-module \( M \) with scalar multiplication
  \begin{balign*}
    nu \coloneqq \begin{cases}
      0,              & n = 0  \\
      u + \ldots + u, & n > 0  \\
      -((-n)u),       & n < 0.
    \end{cases}
  \end{balign*}
\end{definition}

\begin{proposition}\label{thm:abelian_group_iff_z_module}\mcite[375]{Knapp2016BasicAlgebra}
  Every abelian group is isomorphic to exactly one \( \BbbZ \)-module.
\end{proposition}
\begin{proof}
  We already saw in \fullref{def:abelian_group_z_module} how every abelian group can be regarded as a \( \BbbZ \)-module. Every \( \BbbZ \)-module can then be identified with its additive group.

  Scalar multiplication ensures that there is exactly one way to define a \( \BbbZ \)-module structure on an abelian group since \( na = (n-1)a + a \) and \( 0a = 0 \).
\end{proof}

\begin{definition}\label{def:left_module_direct_product}
  Let \( \{ X_k \}_{k \in \mscrK} \) be a nonempty family of left \( R \)-modules.

  Analogously to \fullref{def:group_direct_product}, we define their \term{direct product} as the module \( \prod_{k \in \mscrK} X_k \), the operations defined componentwise as
  \begin{balign*}
     & \{ x_k \}_{k \in \mscrK} + \{ y_k \}_{k \in \mscrK}
    \coloneqq
    \{ x_k + y_k \}_{k \in \mscrK},                     \\
     & t \{ x_k \}_{k \in \mscrK}
    \coloneqq
    t \{ t x_k \}_{k \in \mscrK}.
  \end{balign*}

  We define their \term{direct sum} as the submodule of \( \prod_{k \in \mscrK} X_k \) (see \fullref{def:left_module_direct_product}) where only finitely many components of any module element are different from zero.
\end{definition}

\begin{proposition}\label{thm:module_categorical_limits}
  We are interested in \hyperref[def:category_of_cones/limit]{categorical limits} and \hyperref[def:category_of_cones/colimit]{colimits} in the category \( \cat{Mod}_R \) of left-modules over \( R \). Fix an indexed family  \( \{ X_k \}_{k \in \mscrK} \) of \( R \)-modules.
  \begin{thmenum}
    \thmitem{thm:module_categorical_limits/product} Their \hyperref[def:discrete_category_limits]{categorical product} is their direct \hyperref[def:left_module_direct_product]{product} \( \prod_{k \in \mscrK} X_k \), the projection morphisms being inherited from \fullref{thm:discrete_category_limits_in_set}.

    \thmitem{thm:module_categorical_limits/coproduct} Their \hyperref[def:discrete_category_limits]{categorical coproduct} is the \hyperref[def:group_direct_product]{direct sum} \( \oplus_{k \in \mscrK} X_k \), the embedding morphisms being inherited from \fullref{thm:abelian_group_categorical_limits/coproduct}.
  \end{thmenum}
\end{proposition}

\begin{definition}\label{def:linear_combination}
  Let \( M \) be a left \( R \)-module. Like \hyperref[def:polynomial]{polynomials}, we define linear combinations to be tuples \( (t_1, t_2, \ldots, t_n) \) of scalars from \( R \). Unlike with polynomials, we are not interested in defining operations on them, but rather defining the function
  \begin{equation}\label{def:linear_combination/function}
    (x_1, \ldots, x_n) \mapsto \sum_{k=1}^n t_k x_k.
  \end{equation}

  The scalars \( t_1, \ldots, t_n \) are called the \term{coefficients} of the linear combination. A linear combination is said to be \term{trivial} if all coefficients are equal to \( 0_R \).

  For convenience, given set of vectors \( x_1, \ldots, x_n \in M \), we also call the sum \( \sum_{k=1}^n t_k x_k \) a linear combination.

  In the special case where \( R \) is a superring of \( \BbbR \), we define the following special types of linear combinations:
  \begin{thmenum}
    \thmitem{def:linear_combination/affine} an \term{affine combination} if \( \sum_{k=1}^n t_k = 1 \).
    \thmitem{def:linear_combination/conic} a \term{conic combination} if all of the coefficients are nonnegative real numbers.
    \thmitem{def:linear_combination/convex} a \term{convex combination} if it is both affine and conic.
  \end{thmenum}
\end{definition}

\begin{definition}\label{def:left_module_linear_dependence}
  Let \( M \) be a left \( R \)-module and let \( A \subseteq M \). We say that the set \( A \) is \term{linearly independent} if for any linear \hyperref[def:linear_combination/function]{combination}, the equality
  \begin{equation*}
    \sum_{i=1}^n t_i x_k = 0_M
  \end{equation*}
  for \( x_1, \ldots, x_n \in A \) implies that the combination is trivial.

  We say that the vectors \( x_1, \ldots, x_n \) are linearly independent if the corresponding set \( \{ x_1, \ldots, x_n \} \) is linearly independent.

  We say that \( A \) is \term{linearly dependent} if it is not linearly independent.
\end{definition}

\begin{definition}\label{def:left_module_hamel_basis}
  A subset \( B \) of the left \( R \)-module \( M \) is called a \term{Hamel basis} or simply \term{basis} of \( M \) if \( B \) is a \hyperref[def:partially_ordered_set_extremal_points/maximal_and_minimal_element]{minimal} (with respect to set inclusion) linearly independent subset of \( M \).
\end{definition}

\begin{definition}\label{def:free_left_module}\mcite[377]{Knapp2016BasicAlgebra}
  We say that the left \( R \)-module \( M \) is a \term{free left module} if it has a \hyperref[def:left_module_hamel_basis]{basis}.

  Let \( S \) be any set. If we regard \( R \) as a left module over itself, then the \hyperref[thm:module_categorical_limits/coproduct]{direct sum}
  \begin{equation*}
    F(S) \coloneqq \oplus_{s \in S} R
  \end{equation*}
  with injections \( \{ \iota_s \}_{s \in S} \) is called the free left module \term{generated by \( S \)}. Define the function
  \begin{balign*}
     & \varphi: S \to F(S)                \\
     & \varphi(s) \coloneqq \iota_s(1_R).
  \end{balign*}

  The image \( \varphi(S) \) is then a basis of \( F(S) \).

  The cardinality of the basis of a free left module \( M \) is called the \term{rank} \( \rank M \) of \( M \). \Fullref{thm:left_module_basis_cardinality} tells us that this rank is unique for commutative unital rings. If the rank of a module is finite, we say that the module is \term{finitely generated}.

  We also denote \( F(S) = \braket S \), especially in finitely generated modules.
\end{definition}

\begin{proposition}\label{thm:free_module_is_free_functor}
  The functor \( F: \cat{Set} \to \cat{Mod}_R \), defined pointwise in \fullref{def:free_left_module}, is \hyperref[def:category_adjunction]{free}.
\end{proposition}

\begin{proposition}\label{thm:left_module_basis_decomposition}
  Let \( B \) be a basis of the free left \( R \)-module \( M \). Then each element \( u \) of \( M \) can be uniquely (up to a permutation) represented as a linear \hyperref[def:linear_combination]{combination} of elements of \( B \).
\end{proposition}
\begin{proof}
  Let
  \begin{equation*}
    u = \sum_{i=1}^n t_i v_i
  \end{equation*}
  and
  \begin{equation*}
    u = \sum_{j=1}^m s_i w_i
  \end{equation*}
  be two representations of \( u \) as a linear combination over \( B \).

  Define the function
  \begin{balign*}
     & t: M \to R                                \\
     & t(v) \coloneqq \begin{cases}
      t_i, & v = v_i,                          \\
      0,   & v \not\in \{ v_1, \ldots, v_n \}.
    \end{cases}
  \end{balign*}
  and analogously for \( s: M \to R \). Define the set
  \begin{equation*}
    B' \coloneqq \{ v_1, \ldots, v_n, w_1, \ldots, w_m \}.
  \end{equation*}

  Thus,
  \begin{equation*}
    u = \sum_{b \in B'} t(b) b = \sum_{b \in B'} s(b) b
  \end{equation*}
  and
  \begin{equation*}
    0 = u - u = \sum_{b \in B'} (t(b) - s(b)) b.
  \end{equation*}

  The set \( B' \) is linearly independent as a subset of the basis \( B \), hence only a trivial linear combination can be the zero vector. This gives us
  \begin{equation*}
    t(b) = s(b), b \in B'.
  \end{equation*}

  In particular, the two decompositions of \( u \) along \( B \) are identical up to a permutation.
\end{proof}

\begin{definition}\label{def:left_module_basis_projection}
  Let \( M \) be a left \( R \)-module and let \( B \) be a basis of \( M \). For each \( b \in B \), we define the \text{coordinate projection functional} \( \pi_b: M \to R \) that gives us the unique coefficient in the basis decomposition. Thus, for every \( x \in M \) we have
  \begin{equation*}
    x = \sum_{b \in B} \pi_b(x) b.
  \end{equation*}

  The sum is well-defined since only finitely many terms are nonzero.

  When the basis \( B \) is finite and ordered:
  \begin{equation*}
    B = \{ b_1, \ldots, b_n \},
  \end{equation*}
  we also write
  \begin{equation*}
    x = \sum_{i=1}^n x_k b_i.
  \end{equation*}
\end{definition}
\begin{proof}
  By \fullref{thm:left_module_basis_decomposition}, this decomposition is unique given a basis \( B \).
\end{proof}

\begin{proposition}\label{thm:left_module_basis_projections_are_linear}
  The basis projection \hyperref[def:left_module_basis_projection]{maps} are linear.
\end{proposition}
\begin{proof}
  \SubProofOf{def:linear_operator/homogeneity} Let \( t \in R \) and \( x \in M \). We have the unique decompositions
  \begin{balign*}
    x  & = \sum_{b \in B} \pi_b(x) b,  \\
    tx & = \sum_{b \in B} \pi_b(tx) b.
  \end{balign*}

  Since both decompositions have only finitely many terms, their difference also has only finitely many nonzero terms. Thus,
  \begin{equation*}
    0
    =
    tx - tx
    =
    t \left( \sum_{b \in B} \pi_b(x) b \right) - \sum_{b \in B} \pi_b(tx) b
    =
    \sum_{b \in B} (t \pi_b(x) - \pi_b(tx)) b.
  \end{equation*}

  Since the vectors in \( B \) are linearly independent, no nontrivial linear combination can equal the zero vector. Thus, for all \( b \in B \),
  \begin{equation*}
    t \pi_b(x) = \pi_b(tx).
  \end{equation*}

  \SubProofOf{def:linear_operator/additivity} Analogous.
\end{proof}

\begin{theorem}\label{thm:linear_map_iff_function_on_basis}
  Let \( M \) and \( N \) be left \( R \)-modules and let \( B \) be a basis of \( M \). Then there exists a bijection between the \hyperref[def:function]{functions} from \( B \) to \( N \) and the module \hyperref[def:linear_operator]{homomorphisms} from \( M \) to \( N \), such that each linear map is an \hyperref[def:multi_valued_function/restriction]{extension} of the corresponding function.
\end{theorem}
\begin{proof}
  Let \( \varphi: M \to N \) be a homomorphism. Define the function
  \begin{balign*}
     & f: B \to N                 \\
     & f(b) \coloneqq \varphi(b).
  \end{balign*}

  Now define the linear map
  \begin{balign*}
     & \hat \varphi: M \to N                                   \\
     & \hat \varphi(x) \coloneqq \sum_{b \in B} \pi_b(x) f(b).
  \end{balign*}

  Since the projections \( \pi_b(x) \) are linear functions by \fullref{thm:left_module_basis_projections_are_linear} and since we only use the value of \( f \) on fixed vectors, it follows that \( \hat \varphi \) is also linear.

  It remains to show that \( \varphi = \hat \varphi \). For each \( x \in M \), by linearity of \( \varphi \) we have
  \begin{equation*}
    \hat \varphi(x)
    =
    \sum_{b \in B} \pi_b(x) f(b)
    =
    \sum_{b \in B} \pi_b(x) \varphi(b)
    =
    \varphi(x).
  \end{equation*}
\end{proof}

\begin{remark}\label{rem:linear_map_iff_function_on_basis}
  \Fullref{thm:linear_map_iff_function_on_basis} is very powerful in that is allows to study linear maps given their value at only a small subset of vectors. The connection between multilinear \hyperref[def:multilinear_function]{maps} and \hyperref[def:left_module_tensor_product]{tensors} is based on this idea.
\end{remark}

\begin{corollary}\label{thm:linear_maps_agree_on_free_module_if_they_agree_on_basis}
  If two linear maps from the free left module \( M \) to \( N \) agree on a basis of \( M \), they agree on the whole module.
\end{corollary}

\begin{proposition}\label{thm:left_module_basis_cardinality}\mcite{ProofWiki:bases_of_free_module_have_same_cardinality}
  All bases in a free left module over a commutative unital ring have the same cardinality.
\end{proposition}

\begin{definition}\label{def:left_module_tensor_product}\mcite[574]{Knapp2016BasicAlgebra}
  Let \( R \) be a unital ring. Let \( M \) be a right \( R \)-module and \( N \) be a left \( R \)-module. Define the free \hyperref[def:free_abelian_group]{abelian group} \( G \) generated by the basis \( M \times N \), that is,
  \begin{equation*}
    G \coloneqq \oplus_{(m,n) \in M \times N} \BbbZ.
  \end{equation*}

  Denote by \( e_{m,n} \) the \( (m,n) \)-th basis vector and by \( t_{m,n} \) the \( (m,n) \)-th coordinate of \( t \in G \) (we can have only a finite amount of nonzero coordinates since \( G \) is a direct sum).

  We can regard \( G \) as a left \( R \)-module with scalar multiplication given by
  \begin{equation*}
    (r t)_{(m,n)} \coloneqq t_{(rm,n)}.
  \end{equation*}

  Let \( H \) be the submodule of \( G \) generated by
  \begin{itemize}
    \item \( e_{(m_1 - m_2, n)} - e_{(m_1,n)} - e_{(m_2,n)} \), \( m_1, m_2, n \in G \)
    \item \( e_{(m, n_1 - n_2)} - e_{(m,n_1)} - e_{(m,n_2)} \), \( m, n_1, n_2 \in G \)
    \item \( e_{(rm,n)} - e_{(m,rn)} \), \( m, n \in G \) and \( r \in R \)
  \end{itemize}

  Define the \term{tensor product} of \( M \) and \( N \) to be the \( R \)-module
  \begin{equation*}
    M \otimes N \coloneqq G / H.
  \end{equation*}
\end{definition}

\begin{theorem}\label{thm:tensor_product_universal_property}\mcite[thm. 10.18]{Knapp2016BasicAlgebra}
  Let \( R \) be a unital ring. Let \( M \) be a right \( R \)-module and \( N \) be a left \( R \)-module and let \( M \otimes N \) be their \hyperref[def:left_module_tensor_product]{tensor product} with \( q: M \times N \to M \otimes N \) being the corresponding quotient map.

  The tensor product \( M \otimes N \) satisfies the following universal mapping property: for every \( R \)-module \( K \) and any bilinear \hyperref[def:multilinear_function]{map} \( f: M \times N \to K \) there exists a unique map \( \hat f: M \otimes N \to K \) such that
  \begin{equation*}
    f = \hat f \circ q,
  \end{equation*}
  that is, the following diagram commutes:

  \begin{alignedeq}\label{thm:tensor_product_universal_property/diagram}
    \text{\todo{Add diagram}}\iffalse\begin{mplibcode}
      beginfig(1);
      input metapost/graphs;

      v1 := thelabel("$M \times N$", origin);
      v2 := thelabel("$M \otimes N$", (2, 0) scaled u);
      v3 := thelabel("$K$", (1, -1) scaled u);

      a1 := straight_arc(v1, v2);
      a2 := straight_arc(v2, v3);
      a3 := straight_arc(v1, v3);

      draw_vertices(v);
      draw_arcs(a);

      label.top("$q$", straight_arc_midpoint of a1);
      label.lrt("$\hat f$", straight_arc_midpoint of a2);
      label.llft("$f$", straight_arc_midpoint of a3);
      endfig;
    \end{mplibcode}\fi
  \end{alignedeq}
\end{theorem}

\begin{proposition}\label{thm:tensor_product_with_underlying_ring}\mcite[677]{Knapp2016BasicAlgebra}
  Let \( R \) be a unital ring (regarded as a right module) and \( B \) be a left \( R \)-module. Their \hyperref[def:left_module_tensor_product]{tensor product} satisfies
  \begin{equation*}
    R \otimes M \cong M.
  \end{equation*}
\end{proposition}

\begin{definition}\label{def:algebra_over_ring}\mcite[408]{Knapp2016BasicAlgebra}
  Let \( R \) be a commutative unital ring. We say that the left \( R \)-module \( A \) is an \( R \)-\term{algebra} if we define an additional bilinear \term{vector multiplication} operation
  \begin{equation*}
    \odot: A \times A \to A
  \end{equation*}
  such that for \( x, y \in M \) and \( t \in R \)
  \begin{equation*}
    t \cdot (x \odot y) = (t \cdot x) \odot y = x \odot (t \cdot y).
  \end{equation*}

  Both vector and scalar multiplication are usually denoted by juxtaposition.

  If \( \odot \) is associative, commutative, unital or invertible, we add this prefix to \( A \), e.g. A is a commutative algebra if \( \odot \) is commutative.
\end{definition}

\begin{proposition}\label{thm:functions_over_ring_form_algebra}
  Let \( X \) be an arbitrary nonempty set and \( R \) be a commutative unital ring. Define
  \begin{equation*}
    A \coloneqq \cat{Set}(X, R)
  \end{equation*}
  to be the set of all functions from \( X \) to \( R \) (see \fullref{def:category_of_small_sets}). Then \( A \) is an \( R \)-algebra with the operations being defined pointwise, that is,
  \begin{balign*}
    [f + g](x)     & \coloneqq f(x) + g(x)     \\
    [f \odot g](x) & \coloneqq f(x) \circ g(x) \\
    [rf](x)        & \coloneqq r f(x)
  \end{balign*}

  We call the algebra \( A \) the \term{algebra of functions} from \( X \) to \( R \).

  If \( X \) itself has a ring structure, we consider the set of ring \hyperref[thm:ring_homomorphism_simpler_conditions]{homomorphisms}
  \begin{equation*}
    \cat{Ring}(X, R),
  \end{equation*}
  which is usually a strict subset of \( \cat{Set}(X, R) \). This set is usually denoted by \( \hom(X, R) \).

  If \( R \) is a module, but not necessarily a ring, then \( \cat{Set}(X, R) \) is a only module since we do not necessarily have multiplication. See \fullref{def:linear_operator}.
\end{proposition}

\begin{definition}\label{def:homogenous_function}
  Let \( M \) and \( N \) be left \( R \)-modules. We say that the function \( f: M \to N \) is homogeneous with degree \( n \) if for all \( t \in R \) and \( x \in M \) we have
  \begin{equation*}
    f(t x) = t^n f(x).
  \end{equation*}
\end{definition}

\begin{definition}\label{def:left_module_annihilator}\mcite[30]{КоцевСидеров2016}
  Fix a subset \( S \subseteq M \) of a left \( R \)-module \( M \). We define the \term{annihilator} of \( S \) as the ideal
  \begin{equation*}
    \op{ann}(S) \coloneqq \{ r \in R \colon rS = \{ 0 \} \}.
  \end{equation*}
\end{definition}


\chapter[Linear Programming]{Linear Programming}\label{chp:linear_programming}

% Introduction
\chapterinitial{F}{inding} the best configuration of some system can be
challenging, especially when there is a seemingly endless amount of possible
solutions. Optimisation techniques are a way to mathematically derive solutions
that maximise or minimise some objective function, subject to a number of
feasibility constraints. When all components of the problem can be written in a
linear way, then linear programming\index{linear programming} is one technique
that can be used to find the solution.

\section{Problem}\label{sec:linear_programming_problem}

A university runs 14 modules over three subjects: Art, Biology, and Chemistry.
Each subject runs core modules and optional modules.
Table~\ref{tab:modules} gives the module numbers for each of these.

\begin{table}
\begin{center}
\begin{tabular}{lll}
\toprule
\textbf{Art Core} & \textbf{Biology Core} & \textbf{Chemistry Core} \\
\midrule
M00 & M05 & M09 \\
M01 & M06 & M10 \\
\midrule
\textbf{Art Optional} & \textbf{Biology Optional} & \textbf{Chemistry Optional}\\
\midrule
M02 & M07 & M11 \\
M03 & M08 & M12 \\
M04 &     & M13 \\
\bottomrule
\end{tabular}
\end{center}
\caption{List of modules on offer at the university.}
\label{tab:modules}
\end{table}

The university is required to schedule examinations for each of these modules.
The university would like the exams to be scheduled using the least amount of
time slots. However not all modules can be scheduled at the same time
as they share some students:

\begin{itemize}
  \item All art modules share students;
  \item all biology modules share students;
  \item all chemistry modules share students;
  \item biology students have the option of taking optional modules from
  chemistry, so all biology modules may share students with optional chemistry
  modules;
  \item chemistry students have the option of taking optional modules from
  biology, so all chemistry modules may share students with optional biology
  modules;
  \item biology students have the option of taking core art modules, and so all
  biology modules may share students with core art modules.
\end{itemize}

How can every exam be scheduled with no clashes, using the least amount of
time slots?

\section{Theory}\label{sec:linear_programming_theory}

Linear programming is a method that solves a type of
optimisation\index{optimisation} problem of a number of variables by making use
of some concepts of higher dimensional geometry~\cite{conforti2014integer}.
Optimisation here refers to finding the variable that gives either the maximum
or minimum of some linear function, called the objective function.

Linear programming employs algorithms such as the Simplex
method\index{Simplex method} to efficiently search some feasible convex
region, stopping at the optimum. To do this, an objective function function and
constraints need to be defined.

To illustrate this a classic 2-dimensional example will be used:
\(\pounds 50\) of profit can be made on each
tonne of paint A produced, and \(\pounds 60\) profit on each tonne of paint B
produced. A tonne of paint A needs 4 tonnes of component X and 5 tonnes of
component Y. A tonne of paint B needs 6 tonnes of component X and 4 tonnes of
component Y. Only 24 tonnes of X and 20 tonnes of Y are available per day. How
much of paint A and paint B should be produced to maximise profit?

This is formulated as a linear objective function, representing total profit,
that is to be maximised; and two linear constraints, representing the
availability of components X and Y. They are written as:

\begin{align}
\text{Maximise: } 50 A + 60 B & \label{eqn:objective_paint} \\
\text{Subject to: } & \nonumber \\
4 A + 6 B &\leq 24 \label{eqn:ingredientX} \\
5 A + 4 B &\leq 20 \label{eqn:ingredientY}
\end{align}

Now this is a linear system in 2-dimensional space with coordinates A and B.
These are called the decision variables, what is required are the values of A
and B that optimises the objective function given by
expression~\ref{eqn:objective_paint}.

Inequalities~\ref{eqn:ingredientX} and~\ref{eqn:ingredientY} correspond to the
amount of component X and Y available per day. These, along with the additional
constraints that a negative amount of paint cannot be produced (\(A \geq 0\) and
\(B \geq 0\)), form a convex region, shown in Figure~\ref{fig:paint_lp}.
This shaded region shows the pairs of values of \(A\) and \(B\) which are
feasible, that is they satisfy the constraints.

\begin{figure}
\begin{center}
\includestandalone[width=\textwidth]{assets/paint_LP}
\end{center}
    \caption{Visual representation of the paint linear programming
    problem. The feasible convex
    region is shaded in grey; the objective function with arbitrary value is shown
in a dashed line.}
\label{fig:paint_lp}
\end{figure}

Expression~\ref{eqn:objective_paint} corresponds to the total profit, which is
the value to be maximised. As a line in 2-dimensional space,
this expression fixes its gradient, but its value determines the size of the
\(y\)-intercept. Therefore optimising this function corresponds to pushing a line
with that gradient to its furthest extreme within the feasible region,
demonstrated in Figure~\ref{fig:paint_lp}.
Therefore for this problem the optimum occurs in a particular vertex of the
feasible region, at \(A = \frac{12}{7}\) and \(B = \frac{20}{7}\).

This works well as \(A\) and \(B\) can take any real value in the feasible region.
Some problems must be formulated as integer linear
programming\index{integer linear programming} problems where the decision
variables are restricted to integers. There are a number of methods that can
help adapt a real solution to an integer solution. These include cutting
planes\index{cutting planes}, which introduce new constraints around the real
solution to force an integer value; and branch and bound\index{branch and bound}
methods, where we iteratively convert decision variables to their closest two
integers and remove any infeasible solutions~\cite{conforti2014integer}.

Both Python and R have libraries that carry out linear and integer
programming algorithms. When solving these kinds of problems, formulating
them as linear systems is the most important challenge.

Consider again the exam scheduling\index{exam scheduling} problem from
Section~\ref{sec:linear_programming_problem} which
will now be formulated as an integer linear programming problem.
Define \(M\) as the set of all modules to be scheduled, and define \(T\) as the set
of possible time slots. At worst each exam is
scheduled for a different day, thus \(|T| = |M| = 14\) in this case.
Let \(\{X_{mt} \text{ for } m \in M \text{ and } t \in T\}\) be a set of binary
decision variables, that is \(X_{mt} = 1\) if module \(m\) is scheduled for time
\(t\), and \(0\) otherwise.

There are six distinct sets of modules in which exams cannot be scheduled
simultaneously: \(A_c\), \(A_o\) representing core and optional art modules
respectively; \(B_c\), \(B_o\) representing core and optional biology modules
respectively; and \(C_c\), \(C_o\) representing core and optional chemistry modules
respectively.
Therefore \(M = A_c \cup A_o \cup B_c \cup B_o \cup C_c \cup C_o\).

Additionally there are further clashes between these sets:
\begin{itemize}
  \item No modules in \(A_c \cup A_o\) can be scheduled together as they may
  share students, this is given by the constraint in inequality~\ref{eqn:clique1}.
  \item No modules in \(B_c \cup B_o \cup A_c\), can be scheduled together as
  they may share students, given by inequality~\ref{eqn:clique2}.
  \item No modules in \(B_c \cup B_o \cup C_o\), can be scheduled together as
  they may share students, given by inequality~\ref{eqn:clique3}.
  \item No modules in \(B_o \cup C_c \cup C_o\), can be scheduled together as
  they may share students, given by inequality~\ref{eqn:clique4}.
\end{itemize}

Define \(\{Y_t \text{ for } t \in T\}\) as a set of auxiliary binary
decision variables, where \(Y_t\) is 1 if time slot \(t\) is being used. This is
enforced by Inequality~\ref{eqn:auxiliary}.

Equation~\ref{eqn:schedule_all_modules},
ensures all modules are scheduled once and once only.
Thus altogether the formulation becomes:

\begin{align}
\text{Minimise: } \sum_{t \in T} Y_j & \label{eqn:objective_modules} \\
\text{Subject to: } & \nonumber \\
\frac{1}{|M|} \sum_{m \in M} X_{mt} &\leq Y_j \text{ for all } j \in T \label{eqn:auxiliary} \\
\sum_{t \in T} X_{mt} &= 1 \text{ for all } m \in M \label{eqn:schedule_all_modules} \\
\sum_{m \in A_c \cup A_o} X_{mt} &\leq 1 \text{ for all } t \in T \label{eqn:clique1} \\
\sum_{m \in B_c \cup B_o \cup A_c} X_{mt} &\leq 1 \text{ for all } t \in T \label{eqn:clique2} \\
\sum_{m \in B_c \cup B_o \cup C_o} X_{mt} &\leq 1 \text{ for all } t \in T \label{eqn:clique3} \\
\sum_{m \in B_o \cup C_c \cup C_o} X_{mt} &\leq 1 \text{ for all } t \in T \label{eqn:clique4}
\end{align}

Another common way to define this linear programming problem is by representing the
coefficients of the constraints as a matrix\index{matrix}.
That is:

\begin{align}
\text{Minimise: } c^T Z & \\
\text{Subject to: } & \nonumber \\
A Z & \star b
\end{align}

where \(Z\) is a vector\index{vector} representing the decision variables, \(c\)
is the coefficients of \(Z\) in the objective function, \(A\) is the matrix of the
coefficients of \(Z\) in the constraints, \(b\) is the vector of the right hand
side of the constraints, and \(\star\) represents either \(\leq\), \(=\) or
\(\geq\) as required.

As \(Z\) is a one-dimensional vector of decisions variables,
the matrix \(X\) and the vector \(Y\) can be `flattened' together to form this
new variable.
This is done by first ordering \(X\) then \(Y\), within that
ordering by time slot, then within that ordering by module number.
Therefore:

\begin{align}
Z_{|M|t + m} &= X_{mt}\\
Z_{|M|^2 + m} &= Y_m
\end{align}

where \(t\) and \(m\) are indices starting at 0.
For example \(Z_{17}\) would correspond to \(X_{3, 2}\), the decision variable
representing whether module number 4 is scheduled on day 3; \(Z_{208}\) would
correspond to \(Y_{12}\), the decision variable representing whether there is an
exam scheduled for day 12.

Parameters \(c\), \(A\), and \(b\) can be determined by using this same conversion
from the model in Equations~\ref{eqn:objective_modules} to \ref{eqn:clique4}.
The vector \(c\) would be \(|M|^2\) zeroes followed by \(|M|\) ones.
The vector \(b\) would be zeroes for all the rows representing
Equation~\ref{eqn:auxiliary}, and ones for all other constraints.

\section{Solving with Python}\label{sec:linear_programming_solving-with-python}

In this book the Python library Pulp\index{Pulp}~\cite{mitchell2011pulp} will be
used to formulate and solve the integer programming problem. First a function to
create the binary problem variables for a given set of times and modules is needed:

\begin{pyin}
import pulp

def get_variables(modules, times):
    """Returns the binary variables for a given timetabling
    problem.

    Args:
        modules: The complete collection of modules to be
                 timetabled.
        times: The collection of available time slots.

    Returns:
        A tuple containing the decision variables x and y.
    """
    xshape = (modules, times)
    x = pulp.LpVariable.dicts("X", xshape, cat=pulp.LpBinary)
    y = pulp.LpVariable.dicts("Y", times, cat=pulp.LpBinary)
    return x, y
\end{pyin}

The specific modules and times relating to the problem can now be used to
obtain the corresponding variables:

\begin{pyin}
Ac = [0, 1]
Ao = [2, 3, 4]
Bc = [5, 6]
Bo = [7, 8]
Cc = [9, 10]
Co = [11, 12, 13]
modules = Ac + Ao + Bc + Bo + Cc + Co
times = range(14)
x, y = get_variables(modules=modules, times=times)
\end{pyin}

Now \mintinline{python}{y} is a dictionary of binary decision variables, with
keys as elements of the list \mintinline{python}{times}. \(Y_3\) corresponds to
the third day:

\begin{pyin}
print(y[3])
\end{pyin}

\begin{pyout}
Y_3
\end{pyout}

While \mintinline{python}{x} is a dictionary of dictionaries of binary decision
variables, with keys as elements of the lists \mintinline{python}{modules} and
\mintinline{python}{times}. \(X_{2,5}\) is the variable corresponding
to module 2 being scheduled on day 5:

\begin{pyin}
print(x[2][5])
\end{pyin}

\begin{pyout}
X_2_5
\end{pyout}

The next step is to create a Pulp object with the corresponding variables,
objective function, constraints and solve it. This is done with the following
function:

\begin{pyin}
def get_solution(Ac, Ao, Bc, Bo, Cc, Co, times):
    """Returns the binary variables corresponding to the solution
    of given timetabling problem.

    Args:
        Ac: The set of core art modules
        Ao: The set of optional art modules
        Bc: The set of core biology modules
        Bo: The set of optional biology modules
        Cc: The set of core chemistry modules
        Co: The set of optional chemistry modules
        times: The collection of available time slots.

    Returns:
        A tuple containing the decision variables x and y.
    """
    modules = Ac + Ao + Bc + Bo + Cc + Co
    x, y = get_variables(modules=modules, times=times)
    prob = pulp.LpProblem("ExamScheduling", pulp.LpMinimize)
    objective_function = sum([y[day] for day in times])
    prob += objective_function

    M = 1 / len(modules)
    for day in times:
        prob += M * sum(x[m][day] for m in modules) <= y[day]
        prob += sum([x[mod][day] for mod in Ac + Ao]) <= 1
        prob += sum([x[mod][day] for mod in Bc + Bo + Co]) <= 1
        prob += sum([x[mod][day] for mod in Bc + Bo + Ac]) <= 1
        prob += sum([x[mod][day] for mod in Cc + Co + Bo]) <= 1

    for mod in modules:
        prob += sum(x[mod][day] for day in times) == 1

    prob.solve(pulp.apis.PULP_CBC_CMD(msg=False))
    return x, y
\end{pyin}

Using this, the solution \(x\) of the original problem can be obtained:

\begin{pyin}
x, y = get_solution(
    Ac=Ac, Ao=Ao, Bc=Bc, Bo=Bo, Cc=Cc, Co=Co, times=times
)
\end{pyin}

These can be
inspected, for example \(x_{25}\) is a boolean variable relating to if module 2
is scheduled on the 5th day.

\begin{pyin}
print(x[2][5].value())
\end{pyin}

\begin{pyout}
0.0
\end{pyout}

This was assigned the value 0, and so module 2 was not scheduled for that day.
However, module 2 was scheduled for day 9:

\begin{pyin}
print(x[2][9].value())
\end{pyin}

\begin{pyout}
1.0
\end{pyout}

This was assigned a value of 1, and so module 2 was scheduled for that day.

The following function creates a readable schedule:

\begin{pyin}
def get_schedule(x, y, Ac, Ao, Bc, Bo, Cc, Co, times):
    """Returns a human readable schedule corresponding to the
    solution of given timetabling problem.

    Args:
        Ac: The set of core art modules
        Ao: The set of optional art modules
        Bc: The set of core biology modules
        Bo: The set of optional biology modules
        Cc: The set of core chemistry modules
        Co: The set of optional chemistry modules
        times: The collection of available time slots.

    Returns:
        A string with the schedule
    """
    modules = Ac + Ao + Bc + Bo + Cc + Co

    schedule = ""
    for day in times:
        if y[day].value() == 1:
            schedule += f"\nDay {day}: "
            for mod in modules:
                if x[mod][day].value() == 1:
                    schedule += f"{mod}, "
    return schedule
\end{pyin}

Thus:

\begin{pyin}
schedule = get_schedule(
    x=x,
    y=y,
    times=times,
    Ac=Ac,
    Ao=Ao,
    Bc=Bc,
    Bo=Bo,
    Cc=Cc,
    Co=Co,
)
print(schedule)
\end{pyin}

gives:

\begin{pyout}

Day 0: 1, 12,
Day 5: 0, 13,
Day 6: 11,
Day 7: 4, 6, 10,
Day 8: 3, 5, 9,
Day 9: 2, 7,
Day 13: 8,
\end{pyout}

The order of the days do not matter here, but we 7 days are required in order to
schedule all exams with no clashes, with at most three exams scheduled each day.

\section{Solving with R}\label{sec:linear_programming_solving-with-R}

The R package ROI\index{ROI}, the R Optimization
Infrastructure\index{R Optimization Infrastructure} will be used here. This is
a library of code that acts as a front end to a number of other solvers that
need to be installed externally, allowing a range of optimisation problems to be
solved with a number of different solvers.  The solver that will be used here is
called the CBC~\cite{forrest2005cbc} MILP Solver\index{CBC MILP Solver}, which
needs to be installed. The rcbc\index{rcbc}~\cite{shumacher2021rcbc} package is
also necessary but cannot be installed in the usual way. Installation
instructions for both, depend on the operating system and can be found at the
documentation page for the ROI~\cite{theussl2017roi} package\footnote{As of the
time of writing, this can be found at \url{https://roi.r-forge.r-project.org/installation.html}}.

The ROI package requires that the linear programming problem is
represented in its matrix form, with a one-dimensional array of decision
variables. Therefore the form of the model described at the end of
Section~\ref{sec:linear_programming_theory} will be used.
Functions that define the coefficients of the objective function \(c\), the coefficient
matrix \(A\), the vector of the right hand side of the constraints \(b\), and the
vector of equality or inequalities directions \(\star\) are needed.

First the objective function:

\begin{Rin-no-test}
#' Writes the row of coefficients for the objective function
#'
#' @param n_modules: the number of modules to schedule
#' @param n_days: the maximum number of days to schedule
#'
#' @return the objective function row to minimise
write_objective <- function(n_modules, n_days){
  all_days <- rep(0, n_modules * n_days)
  Ys <- rep(1, n_days)
  append(all_days, Ys)
}
\end{Rin-no-test}

For 3 modules and 3 days:

\begin{Rin-no-test}
objectives <- write_objective(n_modules = 3, n_days = 3)
print(objectives)
\end{Rin-no-test}

Which gives the following array, corresponding to the coefficients of the
array \(Z\) for Equation~\ref{eqn:objective_modules}.

\begin{Rout-no-test}
[1] 0 0 0 0 0 0 0 0 0 1 1 1
\end{Rout-no-test}

The following function is used to write one row of that coefficients matrix, for
a given day, for a given set of clashes, corresponding to
Inequalities~\ref{eqn:clique1} to \ref{eqn:clique4}:

\begin{Rin-no-test}
#' Writes the constraint row dealing with clashes
#'
#' @param clashes: a vector of module indices that all cannot
#'                 be scheduled at the same time
#' @param day: an integer representing the day
#'
#' @return the constraint row corresponding to that set of
#'         clashes on that day
write_X_clashes <- function(clashes, day, n_days, n_modules){
  today <- rep(0, n_modules)
  today[clashes] = 1
  before_today <- rep(0, n_modules * (day - 1))
  after_today <- rep(0, n_modules * (n_days - day))
  all_days <- c(before_today, today, after_today)
  full_coeffs <- c(all_days, rep(0, n_days))
  full_coeffs
}
\end{Rin-no-test}

where \mintinline{R}{clashes} is an array containing the module numbers of a set
of modules that may all share students.

The following function is used to write one row of the coefficients matrix, for
each module, ensuring that each module is scheduled on one day and one day only,
corresponding to Equation~\ref{eqn:schedule_all_modules}:

\begin{Rin-no-test}
#' Writes the constraint row to ensure that every module is
#' scheduled once and only once
#'
#' @param module: an integer representing the module
#'
#' @return the constraint row corresponding to scheduling a
#'         module on only one day
write_X_requirements <- function(module, n_days, n_modules){
  today <- rep(0, n_modules)
  today[module] = 1
  all_days <- rep(today, n_days)
  full_coeffs <- c(all_days, rep(0, n_days))
  full_coeffs
}
\end{Rin-no-test}

The following function is used to write one row of the coefficients matrix
corresponding to the auxiliary constraints of Inequality~\ref{eqn:auxiliary}:

\begin{Rin-no-test}
#' Writes the constraint row representing the Y variable,
#' whether at least one exam is scheduled on that day
#'
#' @param day: an integer representing the day
#'
#' @return the constraint row corresponding to creating Y
write_Y_constraints <- function(day, n_days, n_modules){
  today <- rep(1, n_modules)
  before_today <- rep(0, n_modules * (day - 1))
  after_today <- rep(0, n_modules * (n_days - day))
  all_days <- c(before_today, today, after_today)
  all_Ys <- rep(0, n_days)
  all_Ys[day] = -n_modules
  full_coeffs <- append(all_days, all_Ys)
  full_coeffs
}
\end{Rin-no-test}

Finally the following function uses all previous functions to assemble a
coefficients matrix.
It loops though the parameters for each constraint row required, uses the
appropriate function to create the row of the coefficients matrix, sets the
appropriate inequality direction (\(\leq\), \(=\), \(\geq\)), and the value of
the right hand side.
It returns all three components:

\begin{Rin-no-test}
#' Writes all the constraints as a matrix, column of
#' inequalities, and right hand side column.
#'
#' @param list_clashes: a list of vectors with sets of modules
#         that cannot be scheduled at the same time
#'
#' @return f.con the LHS of the constraints as a matrix
#' @return f.dir the directions of the inequalities
#' @return f.rhs the values of the RHS of the inequalities
write_constraints <- function(list_clashes, n_days, n_modules){
  all_rows <- c()
  all_dirs <- c()
  all_rhss <- c()
  n_rows <- 0

  for (clash in list_clashes){
    for (day in 1:n_days){
      clashes <- write_X_clashes(clash, day, n_days, n_modules)
      all_rows <- append(all_rows, clashes)
      all_dirs <- append(all_dirs, "<=")
      all_rhss <- append(all_rhss, 1)
      n_rows <- n_rows + 1
    }
  }
  for (module in 1:n_modules){
    reqs <- write_X_requirements(module, n_days, n_modules)
    all_rows <- append(all_rows, reqs)
    all_dirs <- append(all_dirs, "==")
    all_rhss <- append(all_rhss, 1)
    n_rows <- n_rows + 1
  }
  for (day in 1:n_days){
    Yconstraints <- write_Y_constraints(day, n_days, n_modules)
    all_rows <- append(all_rows, Yconstraints)
    all_dirs <- append(all_dirs, "<=")
    all_rhss <- append(all_rhss, 0)
    n_rows <- n_rows + 1
  }
  f.con <- matrix(all_rows, nrow = n_rows, byrow = TRUE)
  f.dir <- all_dirs
  f.rhs <- all_rhss
  list(f.con, f.dir, f.rhs)
}
\end{Rin-no-test}

For demonstration, with 2 modules and 2 possible days, with the single
constraint that both modules cannot be scheduled at the same time, then:

\begin{Rin-no-test}
write_constraints(
  list_clashes = list(c(1, 2)),
  n_days = 2,
  n_modules = 2
)
\end{Rin-no-test}

This would give 3 components:

\begin{itemize}
  \item a coefficient matrix of the left hand side of the constraints, \(A\),
  (rows 1 and 2 corresponding to the clash on days 1 and 2, row 3 ensuring
  module 1 is scheduled on one day only, row 4 ensuring module 2 is scheduled on
  one day only, and rows 5 and 6 defining the decision variables \(Y\));
  \item an array of direction of the constraint inequalities, \(\star\);
  \item and an array of the right hand side values of the constraints, \(b\).
\end{itemize}

\begin{Rout-no-test}
[[1]]
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    1    0    0    0    0
[2,]    0    0    1    1    0    0
[3,]    1    0    1    0    0    0
[4,]    0    1    0    1    0    0
[5,]    1    1    0    0   -2    0
[6,]    0    0    1    1    0   -2

[[2]]
[1] "<=" "<=" "==" "==" "<=" "<="

[[3]]
[1] 1 1 1 1 0 0
\end{Rout-no-test}

Now, the problem will be solved.
First some parameters, including the sets of modules that all share
students, that is the list of clashes are needed:

\begin{Rin-no-test}
n_modules = 14
n_days = 14
Ac <- c(0, 1)
Ao <- c(2, 3, 4)
Bc <- c(5, 6)
Bo <- c(7, 8)
Cc <- c(9, 10)
Co <- c(11, 12, 13)
list_clashes <- list(
  c(Ac, Ao),
  c(Bc, Bo, Co),
  c(Bc, Bo, Ac),
  c(Bo, Cc, Co)
)
\end{Rin-no-test}

Then, the functions defined above are used to create the objective function and
the 3 elements of the constraints:

\begin{Rin-no-test}
constraints <- write_constraints(
  list_clashes = list_clashes,
  n_days = n_days,
  n_modules = n_modules
)
f.con <- constraints[[1]]
f.dir <- constraints[[2]]
f.rhs <- constraints[[3]]
f.obj <- write_objective(n_modules = n_modules, n_days = n_days)
\end{Rin-no-test}

Finally, once these objects are in place, the
ROI library is used to construct an optimisation problem object:

\begin{Rin-no-test}
library(ROI)

milp <- OP(
  objective = L_objective(f.obj),
  constraints = L_constraint(L = f.con, dir = f.dir, rhs = f.rhs),
  types = rep("B", length(f.obj)),
  maximum = FALSE
)
\end{Rin-no-test}

This creates an \mintinline{R}{OP} object from our objective row
\mintinline{R}{f.obj}, and our constraints which are made up from the three
components \mintinline{R}{f.con}, \mintinline{R}{f.dir} and
\mintinline{R}{f.rhs}.
When creating this object the \mintinline{R}{types} as binary
variables are indicated (an array of \mintinline{R}{"B"} for each decision
variable).
The objective function is to be minimised so
\mintinline{R}{maximum = FALSE} is used.

Now to solve:

\begin{Rin-no-test}
sol <- ROI_solve(milp)
\end{Rin-no-test}

The solver will output information about the solve process and runtime.

\begin{Rin-no-test}
print(sol$solution)
\end{Rin-no-test}

\begin{Rout-no-test}
  [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0
 [30] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 [59] 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
 [88] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
[117] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0
[146] 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0
[175] 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0
[204] 1 0 1 1 1 0 1
\end{Rout-no-test}

This gives the values of each of the \(Z\) decision variables.
We know the structure of this, that is the first 14 variables are the modules
scheduled for day 1, and so on.
The following code prints a readable schedule:

\begin{Rin-no-test}
#' Gives a human readable schedule corresponding to the
#' solution of a given timetable problem.
#'
#' @param sol: a solution to the timetabling problem
#' @param n_modules: the number of modules to schedule
#' @param n_days: the maximum number of days to schedule
#'
#' @return A string with the schedule
get_schedule <- function(sol, n_days, n_modules){
  schedule = ""
  for (day in 1:n_days){
    if (sol$solution[(n_days * n_modules) + day] == 1){
      schedule <- paste(schedule, "\n", "Day", day, ":")
      for (module in 1:n_modules){
        var <- ((day - 1) * n_modules) + module
        if (sol$solution[var] == 1){
          schedule <- paste(schedule, module)
        }
      }
    }
  }
  schedule
}
\end{Rin-no-test}

Thus:

\begin{Rin-no-test}
schedule <- get_schedule(
  sol = sol,
  n_days = n_days,
  n_modules = n_modules
)
cat(schedule)
\end{Rin-no-test}

gives:

\begin{Rout-no-test}

 "Day 2 : 4 11"
 "Day 6 : 1 12"
 "Day 8 : 7"
 "Day 10 : 8"
 "Day 11 : 3 13"
 "Day 12 : 2 6 9 14"
 "Day 14 : 5 10"
\end{Rout-no-test}

This gives that 7 days are the minimum required to schedule the 14 exams without
clashes, with either 1, 2 or 4 exams scheduled on each day.

\section{Wider context}\label{sec:linear_programming_wider_context}

The overview given here on linear programming covers a wide breath of the subject
although not much depth. For specific algorithmic approaches to the underlying
algorithms and problem types, such as branch and bound\index{branch and bound} and
cutting plane\index{cutting plane} methods as well as some minor extensions
see~\cite{conforti2014integer, sultan2014linear}.

The efficiency of linear programming as well as the ability to model
linear situations imply that it is often used for a variety of applications.
Theatre scheduling as one such application is given
in~\cite{guerriero2011operational}.
Scheduling is indeed a wide ranging sub application of linear programming
which can also be used to schedule sport seasons~\cite{duran2007scheduling}.

Other applications include the transportation
problem\index{transportation problem}~\cite{diaz2014survey} which can be used to
find a best allocation of a fleet of delivery vehicles; the fire station
location problem\index{fire station location problem}~\cite{schreuder1981application}
used to minimise travel times to at-risk areas; and the bin packing\index{bin packing}
problem~\cite{hifi2010linear} in which a number of, possibly irregular,
shapes are packed into the smallest possible number of bins.


\section{Independence Models}

\begin{frame}{Statistical Models}
    \begin{block}{Definition}
    A \emph{statistical model} $\mc{P}$ is the collection of probability distributions, usually parameterised by a function  called a \emph{parametrisation}
    $$ \Theta \ra \mc{P}, \quad \text{given by} \quad \theta \mapsto P_{\theta},\quad \text{so that}\quad \mc{P} = \{ P_{\theta} : \theta \in \Theta \}, $$
    where $\Theta$ is the \emph{parameter space}. $\Theta$ is usually a subset of $\RR^{n}$.
    \end{block}

    \begin{block}{McCullagh, \cite{PM2002}}
    This should be defined using category theory.
    \end{block}

\end{frame}

\begin{frame}{Contingency Tables}

    \begin{itemize}
        \item Classify using two criteria with $r$ and $c$ levels, yielding two random variables $X$ and $Y$.
        \item Note outcomes as $[r] := \{1,\ldots, r\}$, and $[c] := \{ 1, \ldots, c \}$.
    \end{itemize}

    All information about $X$ and $Y$ is contained in the \emph{joint probabilities}:
    $$ p_{ij} = P(X = i; Y = j), \quad i \in [r],\ j \in [c]. $$

    \begin{itemize}
        \item These in turn determine the \emph{marginal probabilities}:
    \end{itemize}
    \vspace*{-1pt}
    \begin{equation*}
        \begin{split}
            p_{i+} &:= \sum_{j = 1}^{c} p_{ij} = P(X = i), \quad i \in [r], \\
            p_{+j} &:= \sum_{i = 1}^{r} p_{ij} = P(Y = j), \quad j \in [c].
        \end{split}
    \end{equation*}

\end{frame}

\begin{frame}
    \begin{block}{Definition}
    \begin{itemize}
        \item Two random variables $X$ and $Y$ are \emph{independent} if the joint probabilities factor as $p_{ij} = p_{i+}\cdot p_{+j}$, for all $i \in [r]$ and $j \in [c]$.
        \item Denote independence of $X$ and $Y$ by $X \indep Y$.
    \end{itemize}
    \end{block}

    \begin{block}{Proposition, \cite{BSSSMD2009}}
        \emph{Two random variables $X$ and $Y$ are independent if and only if the $(r \times c)$-matrix, $p = (p_{ij})$, has rank one.}
    \end{block}

    For a $(2 \times 2)$-table, we thus have:

\begin{center}
\begin{tabular}{lcc}
        & $P(Y=1)$ & $P(Y=2)$\\\hline
$P(X=1)$ & $p_{11}$ & $p_{12}$\\
$P(X=2)$ & $p_{21}$ & $p_{22}$\\\hline
\end{tabular}
$ \ \overset{X \indep Y}{\longleadsto{1.25}} \ p_{11}p_{22} = p_{12}p_{21}. $
\end{center}
\end{frame}

\begin{frame}{Some Geometry}

    Suppose now we select $n$ cases, giving rise to $n$ independent pairs of discrete random variables:
    $$ \begin{pmatrix} X^{(1)} \\ Y^{(1)} \end{pmatrix}, \begin{pmatrix} X^{(2)} \\ Y^{(2)} \end{pmatrix}, \ldots, \begin{pmatrix} X^{(n)} \\ Y^{(n)} \end{pmatrix}, $$
    all drawn from the same distribution, i.e.:
    $$ P( X^{(k)} = i; Y^{(k)} = j ) = p_{ij}, \quad \text{for all } i \in [r],\ j \in [c],\ k \in [n]. $$

    \begin{block}{Probability Simplices}
    Joint probability matrix $p = (p_{ij})$ is an \emph{unknown} element of the $(rc-1)$-dimensional \emph{probability simplex}:
    $$ \Delta_{rc-1} = \Set{ q \in \RR^{r\times c} | q_{ij} \geq 0, \text{ for all } i,j, \text{ and } \sum_{i = 1}^{r}\sum_{j = 1}^{c} q_{ij} = 1 }. $$
    \end{block}

\end{frame}

\begin{frame}

    \begin{block}{Definitions}
        \begin{itemize}
        \item A \emph{statistical model} $\mc{M}$ is a subset of $\Delta_{rc-1}$. It represents the set of all candidates for the unknown distribution $p$.

        \item The \emph{independence model} for $X$ and $Y$ is the set
        $$ \mc{M}_{X \indep Y} := \Set{ p \in \Delta_{rc - 1} | \rank(p) = 1 }. $$
        \end{itemize}
    \end{block}

    $\mc{M}_{X \indep Y}$ is the intersection of $\Delta_{rc-1}$ and the set of all matrices $p = (p_{ij})$ such that
    $$ p_{ij}p_{kl} - p_{il}p_{jk} = 0, \quad (1 \leq i < k \leq r, \text{ and } 1 \leq j < l \leq c). $$

    These are called \emph{Segre varieties} in algebraic geometry.

\end{frame}


\section{Introduction}
% a contextual description of the goals of experiment

In 1921, Michelson and Peace developed the concept of {\it astronomy interferometry} \cite{michel}, measuring the {\it angular diameter} of one the brightness star in the sky, {\it Betelgeuse}, with an {\it optical telescope}. Nowadays,  Michelson and Peace techniques have broad applications in astronomy to measure stellar diameters \cite{sbu}.

{\it Optical} (wavelength ranges of 390 to 750 nanometers) or {\it Radio interference} (wavelength range of hundreds of meters to millimeters) \cite{wiki} can be achieved by superimposing signals from more than one telescope, yielding superior {\it resolutions} than that offered by the telescope alone \cite{brand}. This is due the fact that the orders of magnitude of the wavelengths in the radio electromagnetic spectra are scalables to the {\it baselines distances}. Interferometry is a powerful tool in radio astronomy.

A {\it radio interferometer} measuring an extensive source such as the Sun will show a proportional relation of the dimensions of the experiment (\ie measurable baseline lengths), to the radio wavelength detected, and  to the {\it sizes of fringe}, in the resulting power spectra. In addition, precise values for the baseline lengths can be inferred by observing the power spectra of a point source (\eg an artificial geostationary satellite).

In our experiment, we make use of a two-mirrors  radio interferometer, assembled in the Stony Brook University\cite{sbu}, to measure the angular diameter of the Sun, $\Phi_{sun}$. We show that the Sun is roughly resolved when viewed in {\it single-dish mode}, but it is well resolved when {\it observed interferometrically}.
\bigskip

\subsection{The Geometry of Radio Interferometry}

The principle of {\it superposition of waves}, figure \ref{int}, states that when two or more waves are incident on the same point, the {\it total displacement} at that point is equal to the {\it vector sum of the displacements of the individual waves}. If a crest of a wave meets a crest of another wave of the same frequency at the same point, the {\it magnitude of the displacement} is the {\it sum of the individual magnitudes}, yielding a {\it constructive interference}. If a crest of one wave meets a trough of another wave, the magnitude of the displacements equals the difference in the individual magnitudes, yielding a {\it  destructive interference} \cite{brand}.

\begin{figure}[htb]
\begin{center}
\includegraphics[scale=0.55]{figs/inter.png}
\caption{Interference between two plane wave from a distance source at different angles, from \cite{brand}. The signals are vectorially added, constructively (a), (c), and destructively  (b). }
 \end{center}
\label{int}
\end{figure}

An interferometer will detect and vectorially add signals from two different positions, figure \ref{fig:t}-a. The signal arrives on the two {\it mirrors}, being then mixed in the {\it antenna receiver}. The separation between the two positions (mirrors) is named {\it baseline length}, $B$.

\begin{figure}[htb]
\begin{center}

 \includegraphics[scale=0.45]{figs/time.png}  \includegraphics[scale=0.81]{figs/ele.jpg}
\caption{(left)  a -  Signal detection on two mirrors on a radio telescope separated by a baseline length $B$, from \cite{sbu}. (right) b - Elevation and azimuth angles in the dish.}
\label{fig:t}

\end{center}
\end{figure}

From an arbitrary origin, we define the horizontal variable angle of the telescope pointing, $\theta$, and the fixed horizontal angle coordinates of the object in the sky, $\theta_0$, as in figure \ref{fig:t}. The angle $\theta_0$ will be later related to the actual azimuth, $A$, of the object. It will be also useful to calculate the {\it declination}, $\delta$ of the object (equatorial coordinates), that can obtained from the {\it elevation}, $E$, the {\it azimuth}, and the geographical latitude $\phi$ (horizontal coordinates)\cite{wiki},
\begin{equation}
\sin \delta = \sin \phi \cdot \sin E + \cos \phi \cdot \cos E \cdot \cos A.
\label{dec}
\end{equation}

The signal wavefronts arrive at the telescope in phases because one of the sources is overhead. The {\it time delay}, $\tau$, of the signal at the position 2 (figure \ref{fig:t}-a), is related to difference between these two angles, the baseline length $B$, and the electromagnetic radiation speed, {\it c},
\begin{equation}
\tau = \frac{B \sin(\theta - \theta_0)}{c} \approx \frac{B(\theta - \theta_0)}{c},
\label{t}
\end{equation}
where we use the fact that $\theta-\theta_0\ll1$, such that the Taylor-expansion for small-angle approximation allows $    \sin \theta \approx \theta $.

\bigskip

\subsection{The Physics of Radio Interferometry}

Electromagnetic radiation can be written in terms of their perpendicular {\it electric fields, E}, and {\it magnetic fields, B}, where the last can be simply derived from the first with the {\it Maxwell Equations} \cite{jackson}.

The radio signal is electromagnetic radiation. At the frequency $\nu$, its time-sinusoidal amplitude arriving at position 1 (figure \ref{fig:t}-a) can be described as a plane wave with the  electric field  amplitude \cite{sbu},
$$\mathbf E_1(t) = \mathbf E(\theta_0)   \cos [2\pi \nu t] .$$

The signal arriving at position 2 at that same time has traveled $B \sin(\theta - \theta_0)$ more than $E_1(t)$, and its electric field amplitude  is
$$\mathbf E_2(t) =  \mathbf E(\theta_0)  \cos [2\pi \nu (t-\tau)] .$$

In the  interferometer receiver, the two signals are vectorially added,
$$
\mathbf E_{tot}(t) =  \mathbf   E_1(t) + \mathbf E_2(t),
$$
 and the receiver detects the {\it total power} of the signal, $P(t)$.  The radio frequency, $\nu$, is large compared to a data sampling rate of the receiver, and the total power detected  is {\it time averaged} (or integrated) \cite{sbu} \cite {brand} \cite{jackson},
\begin{eqnarray}
P(t) &=& \langle\mathbf E_{tot}(t)^2 \rangle, \nonumber \\
& =&   E^2(\theta_0) [1+ \cos (2\pi \nu \tau)],  \nonumber \\
P(\theta)&=& E^2(\theta_0) [1+ \cos (2\pi B_{\lambda} (\theta - \theta_0))],
\label{p}
\end{eqnarray}
where
$B_{\lambda} \equiv B/\lambda,$
is the {\it normalized baseline length} to the wavelength $\lambda = c/\nu$ and we have applied the results from equation (\ref{t}).

Considering that the object has an extension in the sky,  we can rewrite equation (\ref{p}) as a continuum total power law,
\begin{eqnarray}
P(\theta) = \int \varepsilon (\theta_0) d\theta_0 \Bigg [1+ \cos \Big (2\pi B_{\lambda} (\theta - \theta_0)\Big)\Bigg ].
\label{p22}
\end{eqnarray}

An astronomical object can have any kind of structure, this will result on many kinds of the {\it energy density distribution},  $\varepsilon(\theta_0)$. The telescope interferometer receiver will measure the  sinusoidal power intensity response of the object given by equation (\ref{p22}), where $\theta$ are the fringes pattern due the interferometry. This quantity will be directly related to the {\it Fourier transformation} of the energy density distribution of the object.

\bigskip

\subsection{Obtaining the Baseline Lengths from a Point Source }

For a punctual source (artificial geostationary satellite), the energy density varying  on $\theta_0$ (as we sweep the telescope)  is a $\delta$-function at the position of the object:
\begin{eqnarray}
 \varepsilon_{sat} (\theta_0) = \varepsilon_0 \delta (\theta_0).
\label{del}
\end{eqnarray}

An expect profile of the total power (amplitude squared) of a point source measured interferometrically can be seen in the figure \ref{bas}. The pattern of this figure can be thought as resolved  as a serie of point sources, slightly displaced, where the interference and the peaks and valleys shrink towards the central value of one.

We insert equation (\ref{del}) into (\ref{p22}) and integrate. As we have mentioned in the beginning of this session,  measurements of a point source yields the accurate measurement of  baseline lengths. The total power is zero in the fringe's minimums (destructive interference, as shown in the figure \ref{int}), becomes
\begin{eqnarray}
P_{sat}(\theta) = \varepsilon_0  \Bigg [1+ \cos \Big (2\pi B_{\lambda} (\theta - \theta_0) \Big ) \Bigg] \equiv 0,
\label{p223}
\end{eqnarray}
meaning
\begin{eqnarray}
2\pi B_{\lambda} (\theta - \theta_0) & =& (2n+1) \pi  \nonumber \\
B_{\lambda} (\theta - \theta_0) & =& \frac{2n+1}{2}. \nonumber
\end{eqnarray}

Therefore, the separation between adjacent null positions (\ie the {\it angular size of the component} we are measuring) gives the value of the baseline lengths,
\begin{eqnarray}
B_{\lambda} \Delta \theta_{12} &=& \frac{2(n_1 - n_2)}{2},\nonumber \\
B^{\Delta \theta}_{\lambda} &\approx& \frac{1}{\Delta \theta}.
\end{eqnarray}
(as we can observe, again in the figure  \ref{bas}). We say it is an approximate value because the rotation of the earth plays a role here, it continuously reorients the telescope, causing the directions of the constructive/destructive interference to scan across the radio source. For instance, the earth rotates $0.5^o$ each every 2 minutes.  This oscillation/modulation should be at a much lower frequency than the radio frequency \cite{brand}.

\begin{figure}[htb]
\centering
 \includegraphics[scale=0.55]{figs/bas.png}
\caption{Example plot of total power as function of telescope pointing, $\theta$ for a point source such as the satellite, from \cite{sbu}. }
\label{bas}
\end{figure}

In addition, with the  actual  declination of the object, $\delta$, from equation (\ref{dec}), the fringe $\Delta \theta$ is the {\it Fourier transform} of the fringe period $\Delta t$.  Considering the angular frequency of earth rotation as $\omega = 15^o / h$, we can also obtain the baseline lengths writing
\begin{equation}
  B^{\Delta t}_{\lambda} = \frac{1}{\cos(\delta) \sin(\omega \Delta t)}.
\label{BB}
\end{equation}

In the rest of this session, we shall write $B_{\lambda}$  for simplicity.

\bigskip

\subsection{Calculating the Angular Diameter of the Sun} \label{visb}

We can rewrite the total power given by equation (\ref{p22}),
$$ P_{sun}(\theta) = \int \varepsilon (\theta_0) d\theta_0 + \int \varepsilon (\theta_0) \cos (2\pi B_{\lambda} (\theta - \theta_0)) d\theta_0.$$

This is the the Fourier transformation of the object's energy density distribution and we can prove it rewriting the above equation in the same fashion as in \cite{sbu}:
$$P_{sun}(\theta) = S_{sun} [1+V(\theta,B_{\lambda})],$$
where
$$ S_{sun} = \int \varepsilon (\theta_0) d\theta_0,$$
is the full spectra of the Sun (obtained as a single dish rather than as interferometer), and
\begin{eqnarray}
V(\theta, B_{\lambda})&=& \frac{1}{S_{sun}} \int \varepsilon (\theta_0) \cos [2\pi B_{\lambda} (\theta-\theta_0)] d\theta_0 ,\nonumber \\
&=& V_{sun} (B_{\lambda}) \cos [2\pi B_{\lambda} \theta],
\end{eqnarray}
 where we gauge the {\it zero-phase position} of the coordinates to eliminate a phase. $V_{sun}$ is the visibility function of the Sun (an amplitude of the Fourier transform a baseline length $B_{\lambda}$),
\begin{equation}
V_{sun}(B_{\lambda}) = \frac{1}{S_{sun}} \int \varepsilon (\theta_0) e^{-i2\pi B_{\lambda} \theta_0}d\theta_0.
\end{equation}

We can compare this to the observed total power law,
$$P_{sun}(\theta) = S_{sun} \Big [1 + V_0(B_{\lambda}) \cos [2\pi B_{\lambda} (\theta - \Delta \theta)]\Big].$$

\bigskip

To extract the power function from an astronomical source, $P_{sun}(\theta)$, we sweep the object by changing the direction of the telescope pointing, $\theta$. We see a  sinusoidal curve plus an {\it offset} proportional to  $\theta$, as shown in the figure \ref{psun}, together with maximum and minimum values for the total power function.

\begin{figure}[htb]
\centering
 \includegraphics[scale=0.55]{figs/psun.png}
\caption{Example plot of total power as function of telescope pointing, $\theta$ for a extended source, such as the Sun, from \cite{sbu}. }
\label{psun}
\end{figure}

It is straightforward to rewrite out results in terms of $P_{max}$ and $P_{min}$,
\begin{equation}
P_{max} = S_{sun} [1 + V_{sun}(B_{\lambda})],
\end{equation}
\begin{equation}
 P_{min} = S_{sun }[1-V_{sun}(B_{\lambda})].
\end{equation}

The {\it visibility function}  is then
\begin{eqnarray}
V_{sun}(B_{\lambda}) &=& \frac{P_{max} - P_{min}}{P_{max} + P_{min}}.
\label{vis}
\end{eqnarray}
which is a {\it sinc} function in term of  the baseline lengths,
\begin{eqnarray}
V_{sun}(B_{\lambda}) &=& \frac{\sin  ( \pi   B_{\lambda} \Phi_{sun}  )}{ \pi B_{\lambda} {\Phi_{sun}}}, \nonumber \\
&\equiv& \mbox{ sinc } (B_{\lambda} {\Phi_{sun}}).
\label{vissinc}
\end{eqnarray}

In this experiment we apply  these properties of the visibility function as a Fourier component of our measurement to obtain the final values of the angular diameter of the sun.


\documentclass[notitlepage, twocolumn]{article}
\usepackage{fullpage}
\usepackage[affil-it]{authblk}
\usepackage{hyperref}
\usepackage{algpseudocode}
\usepackage[]{algorithmicx}
\usepackage{algorithm}
\usepackage{amsfonts}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage[backend=bibtex, style=numeric]{biblatex}
\usepackage{caption}
\usepackage{pdfpages}

\captionsetup[ruled]{labelsep=period}

\makeatletter
\@addtoreset{algorithm}{section}% algorithm counter resets every section
\makeatother

\renewcommand{\thealgorithm}{\thesection.\arabic{algorithm}}

\addbibresource{report.bib}

\def\ft{\mathcal{F}}
\algblockdefx[spawn]{Spawn}{EndSpawn}
[1]{\textbf{spawn process for} #1 \textbf{do}}
{\textbf{end spawn}}

\def\CC{\mathbb{C}}	% Complex

\newcommand{\set}[1]{\lbrace #1 \rbrace}
\newcommand{\paren}[1]{\left( #1 \right)}

\title{\bf
GPU Accelerated Fast Fourier Transform
}

\date{December 2015}
\author{
Matt McCarthy
}
\affil{Christopher Newport University\\
\textbf{CPSC 621 Parallel Processing}\\
\texttt{\href{mailto:matthew.mccarthy.12@cnu.edu}{matthew.mccarthy.12@cnu.edu}}
}

\begin{document}
\nocite{*}
\maketitle

\noindent\textbf{Abstract}
We empirically investigate the performance benefits of parallel fast Fourier transform running on the GPU over a sequential version running on the GPU.

\section{Background}

\subsection{Discrete Fourier Transform}

The discrete Fourier transform is a mathematical transformation that takes a set of Complex-valued signals and outputs a set of Complex-valued frequencies.
For an $n$-dimensional Complex-valued vector $\mathbf{X}$, the discrete Fourier transform $\mathbf{Y}=\mathcal{F}(X)$ is given by
\[
	Y_j = \sum_{k=0}^n x_k \omega^{jk}
\]
where $\omega$ is the $n$-th root of unity, $e^{2\pi i/n}$.
Since $\mathbf{Y}$ is an $n$-dimensional, Complex-valued vector, we can see that the discrete Fourier transform has a complexity of $\Theta(n^2)$.

\subsection{Fast Fourier Transform}

Furthermore, we can split the discrete Fourier transform into even and odd sums for $n=2m$, yielding
\[
	Y_j = \sum_{k=0}^m x_{2k}\omega^{2jk} + \omega^j\sum_{k=0}^m x_{2k+1}\omega^{2jk}
\]
which is two separate discrete Fourier transforms.
Suppose $n=2^k$.
If we iterate this process, we get the following algorithm called the one-dimensional, unordered radix 2, fast Fourier transform in Algorithm 1.1.
\begin{algorithm}
	\caption{Recursive FFT}
	\begin{algorithmic}[1]
		\Function{R-FFT}{$\mathbf{X}$,$\mathbf{Y}$,$n$,$\omega$}
			\If{$n$=1}
				\State $y_0=x_0$
			\Else
				\State Let $\mathbf{Q}=\mathbf{0},\mathbf{T}=\mathbf{0}\in\CC^n$
				\State Let $\mathbf{X_e}=(x_0, x_2,\ldots,x_{n-2})$
				\State Let $\mathbf{X_o}=(x_1, x_3,\ldots,x_{n-1})$
				\State \Call{R-FFT}{$\mathbf{X_e}$,$\mathbf{Q_e}$,$n/2$,$\omega^2$}
				\State \Call{R-FFT}{$\mathbf{X_o}$,$\mathbf{T_o}$,$n/2$,$\omega^2$}
				\ForAll{$j\in\set{0,1,\ldots,n-1}$}
					\State $y_j=q_{j\mod{n/2}}+\omega^i t_{j\mod{n/2}}$
				\EndFor
			\EndIf
		\EndFunction
	\end{algorithmic}
\end{algorithm}

\subsubsection{Cooley Tukey}

Furthermore, we have an iterative formulation of the prior algorithm, called the Cooley Tukey algorithm for one-dimensional, unordered radix 2, fast Fourier transforms.

\begin{algorithm}
	\caption{Cooley-Tukey FFT}
	\begin{algorithmic}[1]
		\Function{I-FFT}{$\mathbf{X}$,$\mathbf{Y}$,$n$}
			\State $t:=\lg n$
			\State $\mathbf{R}=\mathbf{X}$
			\For{$m=0$ to $t-1$}
				\State $\mathbf{S}=\mathbf{R}$
				\For{$l=0$ to $n-1$}
					\State Let $(b_0b_1\ldots b_{t-1})$ be the binary expansion of $l$
					\State $j:=(b_0\ldots b_{m-1}0b_{m+1}\ldots b_{t-1})$
					\State $k:=(b_0\ldots b_{m-1}1b_{m+1}\ldots b_{t-1})$
					\State $r_i:= s_j+s_k\omega^{(b_mb_{m-1}\ldots b_0 0\ldots0)}$
				\EndFor
			\EndFor
			\State $\mathbf{Y}:=\mathbf{R}$
		\EndFunction
	\end{algorithmic}
\end{algorithm}

From this pseudo-code, we can determine the complexity of fast Fourier transform as described by Algorithm 1.2.
We begin by noting that we iterate through the outer loop precisely $\lg n$ times and the inner loop $n$ times.
Therefore our complexity is $T_1=\Theta(n\lg n)$.

\subsection{Parallelization}

For our parallelization, we use a simplified version of the binary exchange algorithm, a parallelization of the Cooley Tukey algorithm designed for use on a hypercube.
Since our implementation runs on a single GPU, any thread can access any memory location via a pointer.
However, this also complicates the matter by introducing a potential for data races.
We solve this by modifying the algorithm to work as follows in Algorithm 1.3.
\begin{algorithm}
	\caption{Parallel FFT}
	\begin{algorithmic}[1]
		\Function{PAR-FFT}{$\mathbf{X}$,$\mathbf{Y}$,$n$}
			\State $t:=\lg n$, $BLK:=n/p$
			\State $\mathbf{R}=\mathbf{X}$
			\State $\mathbf{S}=\mathbf{0}$
			\For{$m=0$ to $t-1$}
				\State Swap pointers $\mathbf{R}$ and $\mathbf{S}$
				\Spawn{$l=0$ to $BLK-1$}
					\For{$c=l\cdot BLK$, to $l\cdot(BLK+1)$}
						\State Let $(b_0b_1\ldots b_{t-1})$ be the binary expansion of $c$
						\State $j:=(b_0\ldots b_{m-1}0b_{m+1}\ldots b_{t-1})$
						\State $k:=(b_0\ldots b_{m-1}1b_{m+1}\ldots b_{t-1})$
						\State $r_i:= s_j+s_k\omega^{(b_mb_{m-1}\ldots b_0 0\ldots0)}$
					\EndFor
				\EndSpawn
				\State \textbf{sync}
			\EndFor
			\State $\mathbf{Y}:=\mathbf{R}$
		\EndFunction
	\end{algorithmic}
\end{algorithm}
In each iteration, we only write to $\mathbf{R}$ and only read from $\mathbf{S}$.
Since we wait until each thread is complete before moving on to the next iteration, we avoid the potential to use old or incorrect data.

If we inspect the parallelization, we see that the outer loop runs $\lg n$ times.
However, the inner loop is ran on $p$ processes, each of which handle $n/p$ iterations.
Therefore, the computation cost is $\Theta(n/p\lg n)$.
Moreover, for communication we simply do two reads from VRAM on each iteration of the inner loop, however some values will be in cache so we may get better performance than that.
Ergo, the communication cost is $O(n/p\lg n)$.
Thus, the parallel runtime will be $T_p = \Theta(n/p \lg n)$ and is cost optimal if and only if $p\leq n$.

\section{Experimental Design}

The goal of the experiment is to empirically determine the effect of parallelization on the runtime of the Cooley Tukey algorithm.
To this end we want to measure the runtime of fast Fourier transform using differing sizes of $n$, the dimension of our Complex valued vector, and $t$, the number of threads.
Let $\mathcal{N}$ be the set of vector dimensions we will test and let $\mathcal{T}$ be the set of thread counts we will test.
Furthermore, we specify that each $n\in\mathcal{N}$ and each $t\in\mathcal{T}$ be powers of two.

For $t=1$, we will run the Cooley Tukey algorithm on random Complex valued vectors of dimension $n$ for each $n\in\mathcal{N}$.
Otherwise, we run our parallelization of the Cooley Tukey algorithm on Complex valued vectors of dimension $n$ for each $n\in\mathcal{N}$.
Moreover, before and after each run, we copy the input to the GPU and the output from the GPU.
We take the system time before copying the input to the GPU and again after copying the output to the host.
We do not check the output for correctness during the test in order to save time, but instead checked the correctness of the algorithm beforehand.

\section{Test Environment}

\subsection{Test System}

The machine used to run the test has an Intel i7 4770k running at 4.2GHz, 16GB of RAM, and a Nvidia GTX 970 with a core clock of 1342MHz and memory clock of 7000MHz.
The computer was running Arch Linux on Linux kernel 4.2.5 with Nvidia driver version 358.16 and CUDA 7.5.

\subsection{Test Program}

Firstly, all code can be located at \url{https://github.com/matt-mccarthy/fast-fourier-transform} in the \verb|fast-fourier| folder.
We also include a version of the code in the Appendix.

We wrote all code in CUDA C++.
Furthermore all code was compiled with the CUDA toolkit 7.5 and GCC 5.2.0 using the command \verb|nvcc| \verb| -std=c++11| \verb|-rdc=true| \verb|-arch=compute_52| \verb|-code=sm_52 -O3|.
The library source file is \verb|src/fast-fourier.cu|, and the sequential and parallel main functions are located in \verb|sequential-fft.cu| and \verb|parallel-fft.cu| respectively.

In both the sequential fast Fourier and parallel fast Fourier, we ran into issues with dynamically allocating memory in a CUDA kernel.
Namely, if we allocated and freed often enough, we ran out of memory.
To avoid this issue entirely, we made all allocations of giant arrays occur in main once and then reused those arrays.
As a note, the \verb|binary_stor| array is where we store the binary representation of our index $l$ for each thread.
While this design is suboptimal (ideally, each thread would create its own so others can not touch it), it works and the performance impact should be neglible for large enough $n$.

Moreover, our GPU code is entirely without any conditional statements except the ones for determining whether or not our for loop is within its bounds.
Another note about the code is that at the end of each iteration of the outer for loop, we call \verb|cudaDeviceSynchronize| which blocks until our \verb|transformer| kernel completes.
We do this in order to ensure syncrhonization across all threads and prevent data races.
Furthermore, the \verb|transformer| kernel must always be called from another kernel that runs on exactly one thread.
We do this in order to keep all code running on the device and prevent useless communication between the CPU and GPU.

For our test we took, $\mathcal{N}=\set{2^{17},2^{19},2^{21},2^{23},2^{25}}$ and $\mathcal{T}=\set{1,4,16,64,256,1024}$, all of which are less than the number of cores on our GTX 970.
We chose $\mathcal{N}$ such that we would get a runtime for 1MB of data on the low end, and 1GB of data on the high end since each complex value is a typedef'd \verb|float2|.
Moreover, we ran each combination of $n$ and $t$ 10 times each.
All runtimes were measured and recorded in milliseconds, and our result files are included in Appendix B.

\section{Results}

\begin{figure}\label{results}
	\centering

	\includegraphics[scale=0.35]{results.png}
	\caption{Runtime vs Array Size}
\end{figure}

\begin{figure}\label{res-table}
	\centering
	\begin{small}
	\[
		\begin{array}{r|c|c|c|c|c}
			& 2^{17} & 2^{19} & 2^{21} & 2^{23} & 2^{25}\\\hline
			2^0& 7.37 & 35.8 & 162 & 773 & 3.42\cdot10^6\\\hline
			2^2& 1.75 & 8.47 & 38.5 & 183  & 813\\\hline
			2^4& .446 & 2.15 & 9.75 & 46.4 & 205.8\\\hline
			2^6& .117 & .552 & 2.51 & 11.9 & 52.8\\\hline
			2^8& .034 & .149 & .669 & 3.14 & 14.3\\\hline
			2^{10}& 0.011 & .046 & .201 & .975 & 4.65
		\end{array}
	\]
	\end{small}
	\caption{The table of runtimes at $(n,t)$ in s}
\end{figure}

Figure 2 lists the runtimes for each $t$ (row index) at size $n$ (column index).
Moreover, the graphs all look like the standard $x\ln x$ curve.
By inspection, we see that $T_{2k}\approx T_{2k+2}/4$, which is exactly what we are looking for.
So naively, we can say that this has a linear speedup.

We also attempted to compute on 1GB of random complex numbers, or precisely, $2^{27}$ complex numbers.
The parallel runtime for $t=1024$ was approximately 22s, however, after many hours of running the sequential version at this $n$ did not complete.
Upon deeper analysis, we learned that with perfect linear speedup, the sequential version would have a runtime roughly 1000 times greater than $T_{1024}$, namely 6 hours.
Due to time constraints, we had to abandon this size of $n$ and make our maximum $2^{25}$.
Furthermore, due to memory constraints, we could not test for any $n\geq 2^{28}$ since we have to allocate two arrays of 2GB each in order to compute on it and our 970 has 4GB VRAM.

\section{Future Work}

In our study, VRAM limitations and time constraints prevented us from harnessing extremely large values of $n$.
One way to extend this study to larger values of $n$ is to simply use GPUs that have more than 4GB of VRAM like the Titan X and simply run tests with larger values of $n$.

Another, more interesting path to explore is to distribute the problem across multiple GPUs.
If we split it smartly, we should be able to utilize the extra VRAM without incurring too much communication overhead.
Of course, the inter-GPU communication is also a factor and we would need to decide the best way to manage it.
This latter method can also scale to a GPU farm.

\section{Conclusion}

Our analysis of the algorithm determined that for $t \leq n$, we should get perfect linear speedup and the data provides empirical evidence for that, with 1024 threads executing approximately 1000 times faster than the sequential.

\printbibliography

\end{document}


% !TEX root = main.tex

An important problem in statistics is to estimate the distribution of a random variable from a set of observations. For example, certain observations may be known (or assumed) to have an exponential distribution and we want to estimate the rate parameter $\lambda$ of the distribution, or observations may be taken from a normal distribution and we want to estimate the mean $\mu$ and variance $\sigma^2$ of the distribution.

%-------------------------------------------------
\section{Random samples}\label{sec:random_samples}

First we define random vectors. These are simply vectors of random variables.

\begin{definition}\label{def:random_vector}
Let $X_1,X_2,\ldots,X_n$ be random variables defined on the same probability space. The vector-valued function
\[
\begin{array}{rccl}
\mathbf{X}:	& \Omega 	& \longrightarrow 		& \R^n \\
			& \omega	& \mapsto	& \big[X_1(\omega), X_2(\omega),\ldots, X_n(\omega)\big]
\end{array}
\]
is called a \emph{random vector} of size $n$. The individual $X_i$ are called the \emph{component variables} of $\mathbf{X}$ and a vector $\mathbf{x} = (x_1,x_2\ldots,x_n)\in\R^n$ is called a \emph{realisation} of $\mathbf{X}$, where $x_i$ is the value taken by the corresponding component variable $X_i$.
\end{definition}

The behaviour of a random vector is completely described by the joint CDF of its component variables.
\begin{definition}
Let $\mathbf{X}=(X_1,X_2,\ldots,X_n)$ be a random vector.
\ben
\it
The \emph{joint distribution} of $\mathbf{X}$ is the function $\prob_{\mathbf{X}}(B) = \prob(\mathbf{X}\in B)$ defined on subsets of $\R^n$.
\it
The \emph{joint CDF} of $\mathbf{X}$ is the function $F_{\mathbf{X}}:\R^n\to[0,1]$ given by
\begin{align*}
F_{\mathbf{X}}(\mathbf{x}) 	= \prob(\mathbf{X}\leq\mathbf{x})
							& = \prob(X_1\leq x_1, X_2\leq x_2, \ldots, X_n\leq x_n) \\
%							& = \prob\big(\{\omega:X_1(\omega)\leq x_1, X_2(\omega)\leq x_2, \ldots, X_n(\omega)\leq x_n\}\big).
\end{align*}
\een
\end{definition}

In general the component variables $X_i$ might depend on each other and have different distributions. Our analysis is greatly simplified by assuming that the $X_i$ are \emph{independent} and \emph{identically distributed}.% random variables.

\begin{definition}\label{def:random_sample}
Let $X$ be a random variable. A \emph{random sample from the distribution of $X$} is a random vector with the property that the component variables $X_i$ are independent and have the same distribution as $X$.
\end{definition}

By independence the joint CDF of a random sample is just the product of its marginal CDFs and because each $X_i$ has the same distribution as $X$, this can be expressed entirely in terms of the CDF of $X$.

\begin{lemma}
Let $\mathbf{X}=(X_1,X_2,\ldots,X_n)$ be a random sample from the distribution of $X$. Then the joint CDF and joint PMF/PDF of $\mathbf{X}$ can be written respectively as
\[
F_{\mathbf{X}}(\mathbf{x}) = \textstyle\prod_{i=1}^n F_X(x_i)
\qquad\text{and}\qquad
f_{\mathbf{X}}(\mathbf{x}) = \textstyle\prod_{i=1}^n f_X(x_i)
%\qquad\text{respecively},
\]
where $F_X$ and $f_X$ are the CDF and PMF/PDF of $X$ respectively, and $\boldx=(x_1,x_2,\ldots,x_n)$ is a realisation of the sample..
\end{lemma}
\begin{proof}
\begin{align*}
F_{\mathbf{X}}(\mathbf{x})
	& = \prob(X_1\leq x_1, X_2\leq x_2, \ldots, X_n\leq x_n) \\
	& = \prob(X_1\leq x_1)\prob(X_2\leq x_2)\ldots\prob(X_n\leq x_n) \quad\text{(by independence)} \\
	& = F_X(x_1)F_X(x_2)\cdots F_X(x_n) \quad\text{(because each $X_i$ has the same distribution as $X$)}\\
	& = \textstyle\prod_{i=1}^n F_X(x_i).
\end{align*}
\end{proof}

%\begin{example}
%Let $\mathbf{X}=(X_1,X_2,\ldots,X_n)$ be a random sample from the $\text{Exponential}(\lambda)$ distribution, where $\lambda>0$ is a rate parameter. Then the joint CDF and joint PDF of $\mathbf{X}$ are respectively
%\begin{align*}
%F_{\mathbf{X}}(\mathbf{x})
%	& = \textstyle\prod_{i=1}^n\big[1-\exp(-\lambda x_i)\big] \\
%f_{\mathbf{X}}(\mathbf{x})
%	& = \textstyle\prod_{i=1}^n\lambda \exp(\lambda x_i) = \lambda^n\exp(-\lambda\sum_{i=1}^n x_i).
%\end{align*}
%\end{example}
%The same relationship holds for PMFs (discrete case) and PDFs (continuous case).
%\begin{corollary}
%If $\boldX=(X_1,X_2,\ldots,X_n)$ is a random sample from the distribution of $X$, the joint PMF/PDF of $\mathbf{X}$ can be written as
%\[
%f_{\mathbf{X}}(\mathbf{x}) = \textstyle\prod_{i=1}^n f_X(x_i)
%\]
%for all $\boldx=(x_1,x_2,\ldots,x_n)\in\R^n$, where $f_X$ is the PMF/PDF of $X$.
%\end{corollary}