1.. Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht
2
3   Distributed under the terms of the BSD 3-Clause License.
4
5   The full license is in the file LICENSE, distributed with this software.
6
7.. _xtensor-assign-label:
8
9Assignment
10==========
11
12In this section, we consider the class ``xarray`` and its semantic bases (``xcontainer_semantic`` and
13``xsemantic_base``) to illustrate how the assignment works. `xtensor` provides different mechanics of
14assignment depending on the type of expression.
15
16Extended copy semantic
17~~~~~~~~~~~~~~~~~~~~~~
18
19``xarray`` provides an extended copy constructor and an extended assignment operator:
20
21.. code::
22
23    template <class E>
24    xarray(const xexpression<E>&);
25
26    template <class E>
27    self_type& operator=(const xexpression<E>& e);
28
29The assignment operator forwards to ``xsemantic_base::operator=`` whose implementation is given below:
30
31.. code::
32
33    template <class E>
34    derived_type& operator=(const xexpression<E>& e)
35    {
36        temporary_type tmp(e);
37        return this->derived_cast().assign_temporary(std::move(tmp));
38    }
39
40Here ``temporary_type`` is ``xarray``, the assignment operator computes the result of the expression in
41a temporary variable and then assigns it to the ``xarray`` instance. This temporary variable avoids aliasing
42when the array is involved in the rhs expression where broadcasting happens:
43
44.. code::
45
46    xarray<double> a = {1, 2, 3, 4};
47    xarray<double> b = {{1, 2, 3, 4},
48                        {5, 6, 7, 8}};
49    a = a + b;
50
51The extended copy constructor calls ``xsemantic_base::assign`` which calls ``xcontainer::assign_xexpression``.
52This two-steps invocation allows to provide an uniform API (assign, plus_assign, minus_assign, etc) in the
53top base class while specializing the implementations in inheriting classes (``xcontainer_semantic`` and
54``xview_semantic``). ``xcontainer::assign_xexpression`` eventually calls the free function ``xt::assign_xexpression``
55which will be discussed in details later.
56
57The behavior of the extended copy semantic can be summarized with the following diagram:
58
59.. image:: extended_copy_semantic.svg
60
61Computed assignment
62~~~~~~~~~~~~~~~~~~~
63
64Computed assignment can be achieved either with traditional operators (``operator+=``, ``operator-=``) or
65with the corresponding assign functions (``plus_assign``, ``minus_assign``, etc). The computed assignment
66operators forwards to the extended assignment operator as illustrated below:
67
68.. code::
69
70    template <class D>
71    template <class E>
72    inline auto xsemantic_base<D>::operator+=(const xexpression<E>& e) -> derived_type&
73    {
74        return operator=(this->derived_cast() + e.derived_cast());
75    }
76
77The computed assign functions, like ``assign`` itself, avoid the instantiation of a temporary variable.
78They call the overload of ``computed_assign`` which, in the case of ``xcontainer_semantic``, simply forwards
79to the free function ``xt::computed_assign``:
80
81.. code::
82
83    template <class D>
84    template <class E>
85    inline auto xsemantic_base<D>::plus_assign(const xexpression<E>& e) -> derived_type&
86    {
87        return this->derived_cast().computed_assign(this->derived_cast() + e.derived_cast());
88    }
89
90    template <class D>
91    template <class E>
92    inline auto xcontainer_semantic<D>::computed_assign(const xexpression<E>& e) -> derived_type&
93    {
94        xt::computed_assign(*this, e);
95        return this->derived_cast();
96    }
97
98Again this two-steps invocation allows to provide a uniform API in ``xsemantic_base`` and specializations
99in the inheriting semantic classes. Besides this allows some code factorization since the assignment
100logic is implemented only once in ``xt::computed_assign``.
101
102Scalar computed assignment
103~~~~~~~~~~~~~~~~~~~~~~~~~~
104
105Computed assignment operators involving a scalar are similar to computed assign methods:
106
107.. code::
108
109    template <class D>
110    template <class E>
111    inline auto xsemantic_base<D>::operator+=(const E& e) -> disable_xexpression<E, derived_type&>
112    {
113        return this->derived_cast().scalar_computed_assign(e, std::plus<>());
114    }
115
116    template <class D>
117    template <class E, class F>
118    inline auto xcontainer_semantic<D>::scalar_computed_assign(const E& e, F&& f) -> derived_type&
119    {
120        xt::scalar_computed_assign(*this, e, std::forward<F>(f));
121        return this->derived_cast();
122    }
123
124The free function ``xt::scalar_computed_assign`` contains optimizations specific to scalars.
125
126Expression assigners
127~~~~~~~~~~~~~~~~~~~~
128
129The three main functions for assigning expressions (``assign_xexpression``, ``computed_assign`` and
130``scalar_computed_assign``) have a similar implementation: they forward the call to the
131``xexpression_assigner``, a template class that can be specialized according to the expression
132tag:
133
134.. code::
135
136    template <class E1, class E2>
137    inline void assign_xexpression(xexpression<E1>& e1, const xexpression<E2>& e2)
138    {
139        using tag = xexpression_tag_t<E1, E2>;
140        xexpression_assigner<tag>::assign_xexpression(e1, e2);
141    }
142
143    template <class Tag>
144    class xexpression_assigner : public xexpression_assigner_base<Tag>
145    {
146    public:
147
148        using base_type = xexpression_assigner_base<Tag>;
149
150        template <class E1, class E2>
151        static void assign_xexpression(xexpression<E1>& e1, const xexpression<E2>& e2);
152
153        template <class E1, class E2>
154        static void computed_assign(xexpression<E1>& e1, const xexpression<E2>& e2);
155
156        template <class E1, class E2, class F>
157        static void scalar_computed_assign(xexpression<E1>& e1, const E2& e2, F&& f);
158
159        // ...
160    };
161
162`xtensor` provides specializations for ``xtensor_expression_tag`` and ``xoptional_expression_tag``.
163When implementing a new function type whose API is unrelated to the one of ``xfunction_base``,
164the ``xexpression_assigner`` should be specialized so that the assignment relies on this specific API.
165
166assign_xexpression
167~~~~~~~~~~~~~~~~~~
168
169The ``assign_xexpression`` methods first resizes the lhs expression, it chooses an assignment
170method depending on many properties of both lhs and rhs expressions. One of these properties, computed
171during the resize phase, is the nature of the assignment: trivial or not. The assignment is said to be
172trivial when the memory layout of the lhs and rhs are such that assignment can be done by iterating over
173a 1-D sequence on both sides. In that case, two options are possible:
174
175- if ``xtensor`` is compiled with the optional ``xsimd`` dependency, and if the layout and the
176  ``value_type`` of each expression allows it, the assignment is a vectorized index-based loop
177  operating on the expression buffers.
178- if the ``xsimd`` assignment is not possible (for any reason), an iterator-based loop operating
179  on the expresion buffers is used instead.
180
181These methods are implemented in specializations of the ``trivial_assigner`` class.
182
183When the assignment is not trivial, :ref:`stepper-label` are used to perform the assignment. Instead of
184using ``xiterator`` of each expression, an instance of ``data_assigner`` holds both steppers and makes
185them step together.
186
187.. image:: assign_xexpression.svg
188
189computed_assign
190~~~~~~~~~~~~~~~
191
192The ``computed_assign`` method is slightly different from the ``assign_xexpression`` method. After
193resizing the lhs member, it checks if some broadcasting is involved. If so, the rhs expression is
194evaluated into a temporary and the temporary is assigned to the lhs expression, otherwise rhs is
195directly evaluated in lhs. This is because a computed assignment always implies aliasing (meaning
196that the lhs is also involved in the rhs): ``a += b;`` is equivalent to ``a = a + b;``.
197
198.. image:: computed_assign.svg
199
200scalar_computed_assign
201~~~~~~~~~~~~~~~~~~~~~~
202
203The ``scalar_computed_assign`` method simply iterates over the expression and applies the scalar
204operation on each value:
205
206.. code::
207
208    template <class Tag>
209    template <class E1, class E2, class F>
210    inline void xexpression_assigner<Tag>::scalar_computed_assign(xexpression<E1>& e1, const E2& e2, F&& f)
211    {
212        E1& d = e1.derived_cast();
213        std::transform(d.cbegin(), d.cend(), d.begin(),
214                       [e2, &f](const auto& v) { return f(v, e2); });
215    }
216
217