% -*- coding: utf-8 -*-
%
% doc/tex/funcs.tex
%-------------------------------------------------- part of the fastmat demos
%
% Author      : sempersn
% Introduced  : 
%------------------------------------------------------------------------------
%  
%  Copyright 2016 Sebastian Semper, Christoph Wagner
%      https://www.tu-ilmenau.de/ems/
%
%  Licensed under the Apache License, Version 2.0 (the "License");
%  you may not use this file except in compliance with the License.
%  You may obtain a copy of the License at
%
%      http://www.apache.org/licenses/LICENSE-2.0
%
%  Unless required by applicable law or agreed to in writing, software
%  distributed under the License is distributed on an "AS IS" BASIS,
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%  See the License for the specific language governing permissions and
%  limitations under the License.
%
%------------------------------------------------------------------------------

Every fast transformation $\bm A$ offers a basic set of functions that can be used externally.

\subsection{Forward projection}
\texttt{A.forward(x)} -- calculates $\bm A \cdot \bm x$ using the method that is efficient for $\bm A$. Note that $\bm A \cdot \bm X = [\bm A \cdot \bm x^1,\dots,\bm A \cdot \bm x^k]$ is also possible and it uses \np{} internal broadcasting whenever possible.

\begin{snippet}
\begin{lstlisting}[language=Python]
# import the packages
import fastmat as fm
import numpy as np

# define a random vector
vec_x = np.random.randn(2**N)

# define a random matrix
mat_X = npr.randn(2**N,k)

# define a hadamard matrix
H = fm.Hadamard(N)

# apply the matrices
vec_y = H.forward(vec_x)
vec_Y = H.forward(mat_X)
\end{lstlisting}

Assume we have a vector $\bm x \in \R^{2^N}$ and a matrix $\bm X \in \R^{2^N \times k}$ and a Hadamard matrix $\bm{\mathcal{H}}_N$ of order $N$. Then we calculate
\begin{align}
\bm y = \bm H \cdot \bm x, ~ \mathrm{and} ~ \bm Y = \bm H \cdot \bm X
\end{align}
\end{snippet}

%
%
%
\subsection{Backward projection}
\texttt{A.backward(x)} -- calculates $\bm A^\herm \cdot \bm x$ using the method that is efficient for $\bm A$. Here $\bm A^\trans = \bm A^\herm$, if $\bm A$ is a real valued mapping.

\begin{snippet}
\begin{lstlisting}[language=Python]
# import the packages
import fastmat as fm
import numpy as np

# define a random vector
vec_x = npr.randn(2**N)

# define a random matrix
mat_X = npr.randn(2**N,k)

# define a hadamard matrix
H = fm.Hadamard(N)

# apply the matrices
vec_y = H.backward(vec_x)
vec_Y = H.backward(mat_X)
\end{lstlisting}

Assume we have a vector $\bm x \in \R^{2^N}$ and a matrix $\bm X \in \R^{2^N \times k}$ and a Hadamard matrix $\mathcal{\bm H}_N$ of order $N$. Then we calculate
	\[\bm y = \bm H^\trans \cdot \bm  x, ~ \mathrm{and} ~ \bm Y = \bm H^\trans \cdot \bm X.\]
\end{snippet}

%
%
%
\subsection{Item access} 
\texttt{A[$i,j$]} -- returns $\bm e_i^\trans \cdot \bm A \cdot \bm e_j$, i.e.\ the element $a_{i,j}$.

\begin{snippet}
\begin{lstlisting}[language=Python]
# import the package
import fastmat as fm

#define a Fourier matrix
F = fm.Fourier(N)

#access the (i,j)th entry
f = F[i,j]
\end{lstlisting}

Assume we have a fourier matrix $\bm{\mathcal{F}}_N$. Then we get
	\[\bm f = \mathcal{\bm F}_{N {i,j}}.\]
\end{snippet}

\textit{Note:} Please bear in mind that \fm{} is not optimized for this type of item access and any algorithm making heavy use of this operation will be slowed down significantly.
%
%
%
\subsection{Column access} 
\texttt{A.getCols($[i_1,\dots,i_k]$)} -- returns $\bm a_{i_1}, ... , \bm a_{i_k}$, i.e. the columns of $\bm A$ with the given indices.

\begin{snippet}
\begin{lstlisting}[language=Python]
# import the package
import fastmat as fm

#define a Fourier matrix
F = fm.Fourier(N)

#access the given columns
f = F.getCols([1,2,3])
\end{lstlisting}

Assume a Fourier matrix $\bm{\mathcal{F}}_N$. Then we get
	\[f_1,f_2,f_3.\]
\end{snippet}

\textit{Note:} Please bear in mind that \fm{} is not optimized for this type of item access and any algorithm making heavy use of this operation will be slowed down significantly.
%
%
%
\subsection{Row access} 
\texttt{A.getRows($[i_1,\dots,i_k]$} -- returns $\bm a^{i_1}, ... , \bm a^{i_k}$, i.e. the columns of $\bm A^\trans$ with the given indices.

\begin{snippet}
\begin{lstlisting}[language=Python]
# import the package
import fastmat as fm

#define a Fourier matrix
F = fm.Fourier(N)

#access the given rows
f = F.getRows([1,2,3])
\end{lstlisting}

Assume we have a fourier matrix $\bm{\mathcal{F}}_N$. Then we get
	\[f^1,f^2,f^3.\]
\end{snippet}

\textit{Note:} Please bear in mind that \fm{} is not optimized for this type of item access and any algorithm making heavy use of this operation will be slowed down significantly.
%
%
%
\subsection{Overloaded Operators}
The \fm{} package comes with a variety of overloaded operators to facilitate the production of readable and less cluttered code.

\begin{itemize}
 \item simple scalar multiplication via \texttt{S = 3 * A} for $\bm A$ being an arbitrary \fm{} class instance.
 \item easy summation via \texttt{S = A $\pm$ B} for $\bm A$ and $\bm B$ being arbitrary \fm{} class instances, which returns $\bm S$ as a \texttt{fastmat.Sum} object.
 \item easy product building \texttt{P = A * B} for $\bm A$ and $\bm B$ being arbitrary \fm{} class instances, which returns $\bm P$ as a \texttt{fastmat.Product} object.
 \item easy forward transformation \texttt{y = A * x}, where $\bm x$ and $\bm y$ are \np{} arrays. So the calculation is a shorthand for \texttt{y = A.forward(x)}
 \item easy backward transformation \texttt{y = A.H * x}, where $\bm x$ and $\bm y$ are \np{} arrays. So the calculation is a shorthand for \texttt{y = A.backward(x)}
\end{itemize}

\begin{snippet}
\begin{lstlisting}[language=Python]
# import the package
import fastmat as fm

#define a Fourier matrix
A = fm.Fourier(N)
B = fm.Eye(N)

# cast a scalar multiple
M = 3 * A

# cast a sum
S = A + B

# cast a product
P = A * B

# combine everything
M = (3 * A) - (1j * B)

# easy transformation
y = S * x
\end{lstlisting}
\end{snippet}

%
%
%
\subsection{Conversion to Numpy}
\texttt{A.toarray()} -- returns a $2$D \np{}-array. This array represents the same mapping, but it can only be used with normal matrix multiplications. Use it with care, since it might consume a lot of memory.

\begin{snippet}
\begin{lstlisting}[language=Python]
# import the package
import fastmat as fm

#define a Fourier matrix
F = fm.Fourier(N)

#convert it to a numpy array
arr_F = F.toarray()

#print the new object
print(arr_F)
\end{lstlisting}

Assume we have a Fourier mapping $\bm{\mathcal{F}}_N$. Then we get a standard \np{}-array that contains the Fourier matrix' entries.
\end{snippet}
%
\textit{Note:} Please bear in mind that by doing so, you abandon any profits you get from using \fm{}, but gain all the flexibility offered by \np{} instead. This is especially useful if you need to carry out a lot of item, row or columns accesses during your calculations. 
