File: pitfall.rst

package info (click to toggle)
xtensor 0.25.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, trixie
  • size: 6,476 kB
  • sloc: cpp: 65,302; makefile: 202; python: 171; javascript: 8
file content (175 lines) | stat: -rw-r--r-- 4,965 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
.. Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht

   Distributed under the terms of the BSD 3-Clause License.

   The full license is in the file LICENSE, distributed with this software.

Common pitfalls
===============

xarray initialization
---------------------

.. code::

    xt::xarray<double> a({1, 3, 4, 2});

does not initialize a 4D-array, but a 1D-array containing the values ``1``, ``3``,
``4``, and ``2``.
It is strictly equivalent to

.. code::

    xt::xarray<double> a = {1, 3, 4, 2};

To initialize a 4D-array with the given shape, use the :cpp:func:`xt::xarray::from_shape` static method:

.. code::

    auto a = xt::xarray<double>::from_shape({1, 3, 4, 2});

The confusion often comes from the way :cpp:type:`xt::xtensor` can be initialized:

.. code::

    xt::xtensor<double, 4> a = {1, 3, 4, 2};

In this case, a 4D-tensor with shape ``(1, 3, 4, 2)`` is initialized.

Intermediate result
-------------------

Consider the following function:

.. code::

    template <class C>
    auto func(const C& c)
    {
        return (1 - func_tmp(c)) / (1 + func_tmp(c));
    }

where ``func_tmp`` is another unary function accepting an xtensor expression. You may
be tempted to simplify it a bit:

.. code::

    template <class C>
    auto func(const C& c)
    {
        auto tmp = func_tmp(c);
        return (1 - tmp) / (1 + tmp);
    }

Unfortunately, you introduced a bug; indeed, expressions in *xtensor* are not evaluated
immediately, they capture their arguments by reference or copy depending on their nature,
for future evaluation. Since ``tmp`` is an lvalue, it is captured by reference in the last
statement; when the function returns, ``tmp`` is destroyed, leading to a dangling reference
in the returned expression.

Replacing ``auto tmp`` with ``xt::xarray<double> tmp`` does not change anything, ``tmp``
is still an lvalue and thus captured by reference.

Random numbers not consistent
-----------------------------

Using a random number function from xtensor actually returns a lazy
generator. That means, accessing the same element of a random number
generator does not give the same random number if called twice.

.. code::

    auto gen = xt::random::rand<double>({10, 10});
    auto a0 = gen(0, 0);
    auto a1 = gen(0, 0);

    // a0 != a1 !!!

You need to explicitly assign or eval a random number generator, like so:

.. code::

    xt::xarray<double> xr = xt::random::rand<double>({10, 10});
    auto xr2 = xt::eval(xt::random::rand<double>({10, 10}));

    // now xr(0, 0) == xr(0, 0) is true.

variance arguments
------------------

When :cpp:func:`xt::variance` is passed an expression and an integer parameter, this latter
is not the axis along which the variance must be computed, but the degree of freedom:

.. code::

    xt::xtensor<double, 2> a = {{1., 2., 3.}, {4., 5., 6.}};
    std::cout << xt::variance(a, 1) << std::endl;
    // Outputs 3.5

If you want to specify an axis, you need to pass an initializer list:

.. code::

    xt::xtensor<double, 2> a = {{1., 2., 3.}, {4., 5., 6.}};
    std::cout << xt::variance(a, {1}) << std::endl;
    .. Outputs { 0.666667, 0.666667 }

fixed_shape on Windows
----------------------

Builder functions such as :cpp:func:`xt::empty` or :cpp:func:`xt::ones` accept an initializer list
as argument. If the elements of this list do not have the same type, a
curious compilation error may occur on Windows:

.. code::

    size_t N = 10ull;
    xt::xarray<int> ages = xt::empty<int>({N, 4ul});

    // error: cannot convert argument 1 from 'initializer list'
    // to 'const xt::fixed_shape<> &'

To avoid this compiler bug (for which we don't have a workaround), ensure
all the elements in the initializer list have the same type.

Alignment of fixed-size members
-------------------------------

.. note::

    If you are using ``C++ >= 17`` you should not have to worry about this.

When building with *xsimd* (see :ref:`external-dependencies`), if you define a structure
having members of fixed-size xtensor types, you must ensure that the buffers properly
aligned. For this you can use the macro ``XTENSOR_FIXED_ALIGN`` available in
``xtensor/xtensor_config.hpp``.
Consider the following example:

.. code-block:: cpp

    template <typename T>
    class alignas(XTENSOR_FIXED_ALIGN) Foo
    {
    public:

        using allocator_type = std::conditional_t<XTENSOR_FIXED_ALIGN != 0,
                                                  xt_simd::aligned_allocator<T, XTENSOR_FIXED_ALIGN>,
                                                  std::allocator<T>>;

        Foo(T fac) : m_fac(fac)
        {
            m_bar.fill(fac);
        }

        auto get() const
        {
            return m_bar;
        }

    private:

        xt::xtensor_fixed<T, xt::xshape<10, 10>> m_bar;
        T m_fac;
    };

Whereby it is important to store the fixed-sized xtensor type (in this case ``xt::xtensor_fixed<T, xt::xshape<10, 10>>``) as first member.