\subsection{Spaces and Dimensions}
\label{sec:spaces and dimensions}

The extension to more than one dimension when fitting a model contains some 
ambiguities. The data, limits and the models all have corresponding axes or 
columns that should match. 
To deal with that in a simple, yet consistent manner, \zfit{} has a \zspace{}. 
The 
responsibility of this class is to define and handle the axes or dimensions and 
limits. To 
understand the concept and the \zspace{} itself, three definitions need to be 
made:

\begin{description}
	\item[Observables] The observables are the 
	\textit{named axes of a coordinate system}. A single observable is a string 
	and a list of strings acts as several observables describing a higher 
	dimensional system. In the context of data this is equivalent 
	to columns in a data frame. Observables allow a named, \textit{inter-object}
	identification of axes. Therefore, working with observables allows to work 
	independently of the underlying data ordering.
	\item[Axes] Axes are integers and are used to \textit{enumerate the axes 
	of a coordinate system}. This corresponds to indices of an array and 
	provides the fully order-based mapping necessary for \textit{intra-object} 
	manipulations. For example, a \zdata{} with three 
	columns has three axes, 0, 1, and 2, which can though be reordered so that 
	the corresponding observables match the order of some other observables.
	\item[Limits] A description of boundaries that can be used to define any 
	kind of limits of the axes. Currently only rectangular limits are supported 
	but arbitrary shaped limits will be provided in the future.	
\end{description}

A \zspace{} can be initialized with observables and limits to define a domain. 
When 
it's assigned to an object, it automatically connects the axes of the object 
with the observables from the \zspace{}. More details on the implementation and 
use cases as well as additional functionality for dimensional handling can be 
found in Appendix \ref{appendix:spaces defined}.

\subsubsection{Limits}
\label{sec:multiple limits}

Limits are used in many instances, be it in sampling limits, integration or 
data limits. A \zspace{} not only defines the observables but typically also 
has limits associated with it. 
In one-dimensional fits, limits as seen in the 
example in Sec. \ref{sec:quickstart} 
are needed. Simple limits consist of a tuple for each observable with lower and 
upper limits. For example a \zspace{} in one observable \texttt{x} from $-5$ to 
$3$ can be created like

\begin{center}
\begin{minipage}{\textwidth}
\begin{python}
limits = zfit.Space(obs="x", limits=(-5, 3))
\end{python}
\end{minipage}
\end{center}

This is the simplest way of specifying limits and rather a special case. For 
anything more sophisticated, such as multiple limits or multiple observables, 
either a composition of \zspace s or the more 
general format as explained in Appendix 
\ref{appendix:general limits} has to be used. For example when blinding a 
region, a \zspace{} 
with 
multiple limits can be used.

While the general format is fully specified independent of a \zspace{} and can 
therefore be useful programmatically, a \zspace{} with multiple limits can be 
built 
unambiguously from \zspace s with simple limits by adding them, either through 
a dedicated \zfit{} function or using the addition operator in Python. In 
this way, multiple limits can be created through simple composition and without 
the need of using the more general format.

As an example, let's assume a \zspace{} should be created with the observables 
\texttt{x, y} in the two domains $l_01$ and $l_23$
\begin{align*}
l_01 &= \{(x, y) | x_0 < x < x_1, y_0 < y < y_1\} \\
l_23 &= \{(x, y) | x_2 < x < x_3, y_2 < y < y_3\}.
\end{align*}

We start out creating the domains by specifying the limits in the \textit{x} 
observable

\begin{center}
\begin{minipage}{\textwidth}
\begin{python}
limit_x_01 = zfit.Space(obs="x", limits=(x0, x1))
limit_x_23 = zfit.Space(obs="x", limits=(x2, x3))
limits_x = limit_x_01 + limit_x_23
\end{python}
\end{minipage}
\end{center}

Equivalently \pyth{limits_y} can be composed. Since going to higher dimensions 
is unambiguous with two limits in each space, this can be done using the 
multiplication operator in Python or the function 
\pyth{combine}.\footnote{If a different number of limits were defined, an error 
would be thrown.}

\begin{center}
\begin{minipage}{\textwidth}
\begin{python}
limits_xy = limits_x * limits_y
limits_yx = limits_y * limits_x
\end{python}
\end{minipage}
\end{center}

The difference between \pyth{limits_xy} and \pyth{limits_yx} is the order of 
the observables. In the first case, it's \pyth{["x", "y"]} while for the latter 
it's \pyth{["y", "x"]}. In order to ensure consistency, if the two \zspace s 
already have observables in common, the limits in this observables have to be 
the same. \footnote{This exact behaviour 
of the multiplication and observables is the same if models are multiplied.}
Reordering the \zspace{} is possible as well as extracting a subspace, a 
\zspace{} only defined in subset of the dimensions. More details can be found 
in Appendix \ref{appendix:spaces defined}.