LCOV - code coverage report
Current view: top level - elsa/core - DataContainer.h (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 52 56 92.9 %
Date: 2024-05-16 04:22:26 Functions: 71 81 87.7 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include "TypeTraits.hpp"
       4             : #include "elsaDefines.h"
       5             : #include "ExpressionPredicates.h"
       6             : #include "DataDescriptor.h"
       7             : #include "Error.h"
       8             : #include "FormatConfig.h"
       9             : #include "TypeCasts.hpp"
      10             : #include "ContiguousStorage.h"
      11             : #include "Span.h"
      12             : #include "transforms/FFTPolicy.h"
      13             : #include "Overloaded.hpp"
      14             : #include "NdView.h"
      15             : 
      16             : #include <thrust/complex.h>
      17             : 
      18             : #include <variant>
      19             : #include <memory>
      20             : #include <type_traits>
      21             : 
      22             : namespace elsa
      23             : {
      24             :     /**
      25             :      * @brief class representing and storing a linearized n-dimensional signal
      26             :      *
      27             :      * This class provides a container for a signal that is stored in memory. This signal can
      28             :      * be n-dimensional, and will be stored in memory in a linearized fashion. The information
      29             :      * on how this linearization is performed is provided by an associated DataDescriptor.
      30             :      *
      31             :      * @tparam data_t data type that is stored in the DataContainer, defaulting to real_t.
      32             :      *
      33             :      * @author
      34             :      * - Matthias Wieczorek - initial code
      35             :      * - Tobias Lasser - rewrite, modularization, modernization
      36             :      * - David Frank - added DataHandler concept, iterators, integrated unified memory
      37             :      * - Nikola Dinev - add block support
      38             :      * - Jens Petit - expression templates
      39             :      * - Jonas Jelten - various enhancements, fft, complex handling, pretty formatting
      40             :      */
      41             :     template <typename data_t>
      42             :     class DataContainer
      43             :     {
      44             :     public:
      45             :         /// Scalar alias
      46             :         using Scalar = data_t;
      47             : 
      48             :         using reference = typename ContiguousStorage<data_t>::reference;
      49             :         using const_reference = typename ContiguousStorage<data_t>::const_reference;
      50             : 
      51             :         /// iterator for DataContainer (random access and continuous)
      52             :         using iterator = typename ContiguousStorage<data_t>::iterator;
      53             : 
      54             :         /// const iterator for DataContainer (random access and continuous)
      55             :         using const_iterator = typename ContiguousStorage<data_t>::const_iterator;
      56             : 
      57             :         enum class ImportStrategy {
      58             :             View,
      59             :             HostCopy,
      60             :             DeviceCopy,
      61             :         };
      62             : 
      63             :         /// delete default constructor (without metadata there can be no valid container)
      64             :         DataContainer() = delete;
      65             : 
      66             :         /**
      67             :          * @brief Constructor for empty DataContainer, no initialisation is performed,
      68             :          *        but the underlying space is allocated.
      69             :          *
      70             :          * @param[in] dataDescriptor containing the associated metadata
      71             :          */
      72             :         explicit DataContainer(const DataDescriptor& dataDescriptor);
      73             : 
      74             :         /**
      75             :          * @brief Constructor for DataContainer, initializing it with a DataVector
      76             :          *
      77             :          * @param[in] dataDescriptor containing the associated metadata
      78             :          * @param[in] data vector containing the initialization data
      79             :          */
      80             :         DataContainer(const DataDescriptor& dataDescriptor,
      81             :                       const Eigen::Matrix<data_t, Eigen::Dynamic, 1>& data);
      82             : 
      83             :         /// constructor accepting a DataDescriptor and a DataHandler
      84             :         DataContainer(const DataDescriptor& dataDescriptor,
      85             :                       const ContiguousStorage<data_t>& storage);
      86             : 
      87             :         /// constructor accepting a DataDescriptor and a DataHandler
      88             :         DataContainer(const DataDescriptor& dataDescriptor, ContiguousStorageView<data_t> storage);
      89             : 
      90             :         /// constructor accepting a DataDescriptor and an NdView. Dimensions in the DataDescriptor
      91             :         /// must match the dimensions of the NdView.
      92             :         template <mr::StorageType tag>
      93             :         DataContainer(const DataDescriptor& dataDescriptor, NdViewTagged<data_t, tag>& view);
      94             : 
      95             :         /**
      96             :          * @brief Copy constructor for DataContainer
      97             :          *
      98             :          * @param[in] other DataContainer to copy
      99             :          */
     100             :         DataContainer(const DataContainer<data_t>& other);
     101             : 
     102             :         /**
     103             :          * @brief copy assignment for DataContainer
     104             :          *
     105             :          * @param[in] other DataContainer to copy
     106             :          *
     107             :          * Note that a copy assignment with a DataContainer on a different device (CPU vs GPU) will
     108             :          * result in an "infectious" copy which means that afterwards the current container will use
     109             :          * the same device as "other".
     110             :          */
     111             :         DataContainer<data_t>& operator=(const DataContainer<data_t>& other);
     112             : 
     113             :         /**
     114             :          * @brief Move constructor for DataContainer
     115             :          *
     116             :          * @param[in] other DataContainer to move from
     117             :          *
     118             :          * The moved-from objects remains in a valid state. However, as preconditions are not
     119             :          * fulfilled for any member functions, the object should not be used. After move- or copy-
     120             :          * assignment, this is possible again.
     121             :          */
     122             :         DataContainer(DataContainer<data_t>&& other) noexcept;
     123             : 
     124             :         /**
     125             :          * @brief Move assignment for DataContainer
     126             :          *
     127             :          * @param[in] other DataContainer to move from
     128             :          *
     129             :          * The moved-from objects remains in a valid state. However, as preconditions are not
     130             :          * fulfilled for any member functions, the object should not be used. After move- or copy-
     131             :          * assignment, this is possible again.
     132             :          *
     133             :          * Note that a copy assignment with a DataContainer on a different device (CPU vs GPU) will
     134             :          * result in an "infectious" copy which means that afterwards the current container will use
     135             :          * the same device as "other".
     136             :          */
     137             :         DataContainer<data_t>& operator=(DataContainer<data_t>&& other) noexcept;
     138             : 
     139             :         static DataContainer<data_t> fromRawData(data_t* raw_data, mr::StorageType storageType,
     140             :                                                  const IndexVector_t& shape,
     141             :                                                  const IndexVector_t& strides,
     142             :                                                  const DataDescriptor& desc,
     143             :                                                  std::function<void()> destructor);
     144             : 
     145             :         /// return nd-view describing the current data container
     146             :         NdView<data_t> toNdView() const;
     147             : 
     148             :         /// return the current DataDescriptor
     149             :         const DataDescriptor& getDataDescriptor() const;
     150             : 
     151             :         /// return true, if the current DataContainer is owning its memory
     152             :         bool isOwning() const;
     153             : 
     154             :         /// return true, if the current DataContainer is a view, i.e. is not owning its memory
     155             :         bool isView() const;
     156             : 
     157             :         ContiguousStorage<data_t>& storage();
     158             : 
     159             :         const ContiguousStorage<data_t>& storage() const;
     160             : 
     161             :         /// return the size of the stored data (i.e. the number of elements in the linearized
     162             :         /// signal)
     163             :         index_t getSize() const;
     164             : 
     165             :         /// get the number of blocks the signal is created from. If the descriptor
     166             :         /// is not of type `BlockDescriptor` 1 is returned;
     167             :         index_t getNumberOfBlocks() const;
     168             : 
     169             :         /// return the index-th element of linearized signal (not bounds-checked!)
     170             :         reference operator[](index_t index);
     171             : 
     172             :         /// return the index-th element of the linearized signal as read-only (not bounds-checked!)
     173             :         const_reference operator[](index_t index) const;
     174             : 
     175             :         /// return an element by n-dimensional coordinate (not bounds-checked!)
     176             :         /// The indexing follows the convention `(x1, x2, ..., xn)`. Specifically,
     177             :         /// in 2D `(x, y)` and 3D `(x, y, z)`.
     178             :         reference operator()(const IndexVector_t& coordinate);
     179             : 
     180             :         /// return an element by n-dimensional coordinate as read-only (not bounds-checked!)
     181             :         /// The indexing follows the convention `(x1, x2, ..., xn)`. Specifically,
     182             :         /// in 2D `(x, y)` and 3D `(x, y, z)`.
     183             :         const_reference operator()(const IndexVector_t& coordinate) const;
     184             : 
     185             :         data_t at(const IndexVector_t& coordinate) const;
     186             : 
     187             :         /// return an element by its coordinates (not bounds-checked!)
     188             :         /// The indexing follows the convention `(x1, x2, ..., xn)`. Specifically,
     189             :         /// in 2D `(x, y)` and 3D `(x, y, z)`.
     190             :         template <typename idx0_t, typename... idx_t,
     191             :                   typename = std::enable_if_t<
     192             :                       std::is_integral_v<idx0_t> && (... && std::is_integral_v<idx_t>)>>
     193             :         reference operator()(idx0_t idx0, idx_t... indices)
     194      636709 :         {
     195      636709 :             IndexVector_t coordinate(sizeof...(indices) + 1);
     196      636709 :             ((coordinate << idx0), ..., indices);
     197      636709 :             return operator()(coordinate);
     198      636709 :         }
     199             : 
     200             :         /// return an element by its coordinates as read-only (not bounds-checked!)
     201             :         /// The indexing follows the convention `(x1, x2, ..., xn)`. Specifically,
     202             :         /// in 2D `(x, y)` and 3D `(x, y, z)`.
     203             :         template <typename idx0_t, typename... idx_t,
     204             :                   typename = std::enable_if_t<
     205             :                       std::is_integral_v<idx0_t> && (... && std::is_integral_v<idx_t>)>>
     206             :         const_reference operator()(idx0_t idx0, idx_t... indices) const
     207        2240 :         {
     208        2240 :             IndexVector_t coordinate(sizeof...(indices) + 1);
     209        2240 :             ((coordinate << idx0), ..., indices);
     210        2240 :             return operator()(coordinate);
     211        2240 :         }
     212             : 
     213             :         /// return the dot product of this signal with the one from container other
     214             :         data_t dot(const DataContainer<data_t>& other) const;
     215             : 
     216             :         /// return the squared l2 norm of this signal (dot product with itself)
     217             :         GetFloatingPointType_t<data_t> squaredL2Norm() const;
     218             : 
     219             :         /// return the l2 norm of this signal (square root of dot product with itself)
     220             :         GetFloatingPointType_t<data_t> l2Norm() const;
     221             : 
     222             :         /// return the pointwise l2 norm of this signal. Pointwise norms assume
     223             :         /// blocked DataContainers, and then the norm is taken column-wise.
     224             :         /// The pointwise l2 norm is equivalent to the following NumPy code
     225             :         /// snippet: `np.sqrt(np.sum(x ** 2, axis=0))`, assuming a signal,
     226             :         /// where the first axis indexes each block of the data.
     227             :         DataContainer<GetFloatingPointType_t<data_t>> pL2Norm() const;
     228             : 
     229             :         /// return the l0 pseudo-norm of this signal (number of non-zero values)
     230             :         index_t l0PseudoNorm() const;
     231             : 
     232             :         /// return the l1 norm of this signal (sum of absolute values)
     233             :         GetFloatingPointType_t<data_t> l1Norm() const;
     234             : 
     235             :         /// return the pointwise l1 norm of this signal. Pointwise norms assume
     236             :         /// blocked DataContainers, and then the norm is taken column-wise.
     237             :         ///
     238             :         /// The pointwise l1 norm is equivalent to the following NumPy code
     239             :         /// snippet: `np.sum(np.abs(x), axis=0)`, assuming a signal,
     240             :         /// where the first axis indexes each block of the data.
     241             :         DataContainer<GetFloatingPointType_t<data_t>> pL1Norm() const;
     242             : 
     243             :         /// return the linf norm of this signal (maximum of absolute values)
     244             :         GetFloatingPointType_t<data_t> lInfNorm() const;
     245             : 
     246             :         /// return the mixed L21 norm of this signal
     247             :         data_t l21MixedNorm() const;
     248             : 
     249             :         /// return the mixed L21 norm of this signal with smoothing parameter epsilon
     250             :         data_t l21SmoothMixedNorm(data_t epsilon) const;
     251             : 
     252             :         /// return the sum of all elements of this signal
     253             :         data_t sum() const;
     254             : 
     255             :         /// return the min of all elements of this signal
     256             :         data_t minElement() const;
     257             : 
     258             :         /// return the max of all elements of this signal
     259             :         data_t maxElement() const;
     260             : 
     261             :         /// convert to the fourier transformed signal.
     262             :         /// @param policy controls the choice of implementation. If the requested policy
     263             :         /// cannot be applied, a runtime error is generated.
     264             :         /// @see FFTPolicy
     265             :         void fft(FFTNorm norm, FFTPolicy policy = FFTPolicy::AUTO);
     266             : 
     267             :         /// convert to the inverse fourier transformed signal.
     268             :         /// @param policy controls the choice of implementation. If the requested policy
     269             :         /// cannot be applied, a runtime error is generated.
     270             :         /// @see FFTPolicy
     271             :         void ifft(FFTNorm norm, FFTPolicy policy = FFTPolicy::AUTO);
     272             : 
     273             :         /// copy the values from other DataContainer to this DataContainer
     274             :         void assign(const DataContainer<data_t>& other);
     275             : 
     276             :         /// Set all values of the DataContainer to zero
     277             :         DataContainer<data_t>& zero() &;
     278             : 
     279             :         /// Set all values of the DataContainer to zero
     280             :         DataContainer<data_t> zero() &&;
     281             : 
     282             :         /// Set all values of the DataContainer to one
     283             :         DataContainer<data_t>& one() &;
     284             : 
     285             :         /// Set all values of the DataContainer to one
     286             :         DataContainer<data_t> one() &&;
     287             : 
     288             :         /// Set all values of the DataContainer to the given value
     289             :         DataContainer<data_t>& fill(SelfType_t<data_t> value) &;
     290             : 
     291             :         /// Set all values of the DataContainer to the given value
     292             :         DataContainer<data_t> fill(SelfType_t<data_t> value) &&;
     293             : 
     294             :         /// if the datacontainer is already complex, return itself.
     295             :         DataContainer<add_complex_t<data_t>> asComplex() const;
     296             : 
     297             :         /// compute in-place element-wise addition of another container
     298             :         DataContainer<data_t>& operator+=(const DataContainer<data_t>& dc);
     299             : 
     300             :         /// compute in-place element-wise subtraction of another container
     301             :         DataContainer<data_t>& operator-=(const DataContainer<data_t>& dc);
     302             : 
     303             :         /// compute in-place element-wise multiplication with another container
     304             :         DataContainer<data_t>& operator*=(const DataContainer<data_t>& dc);
     305             : 
     306             :         /// compute in-place element-wise division by another container
     307             :         DataContainer<data_t>& operator/=(const DataContainer<data_t>& dc);
     308             : 
     309             :         /// compute in-place addition of a scalar
     310             :         DataContainer<data_t>& operator+=(data_t scalar);
     311             : 
     312             :         /// compute in-place subtraction of a scalar
     313             :         DataContainer<data_t>& operator-=(data_t scalar);
     314             : 
     315             :         /// compute in-place multiplication with a scalar
     316             :         DataContainer<data_t>& operator*=(data_t scalar);
     317             : 
     318             :         /// compute in-place division by a scalar
     319             :         DataContainer<data_t>& operator/=(data_t scalar);
     320             : 
     321             :         /// assign a scalar to the DataContainer
     322             :         DataContainer<data_t>& operator=(data_t scalar);
     323             : 
     324             :         /// comparison with another DataContainer
     325             :         bool operator==(const DataContainer<data_t>& other) const;
     326             : 
     327             :         /// comparison with another DataContainer
     328             :         bool operator!=(const DataContainer<data_t>& other) const;
     329             : 
     330             :         /// returns a reference to the i-th block, wrapped in a DataContainer
     331             :         DataContainer<data_t> getBlock(index_t i);
     332             : 
     333             :         /// returns a const reference to the i-th block, wrapped in a DataContainer
     334             :         const DataContainer<data_t> getBlock(index_t i) const;
     335             : 
     336             :         /// return a view of this DataContainer with a different descriptor
     337             :         DataContainer<data_t> viewAs(const DataDescriptor& dataDescriptor);
     338             : 
     339             :         /// return a const view of this DataContainer with a different descriptor
     340             :         const DataContainer<data_t> viewAs(const DataDescriptor& dataDescriptor) const;
     341             : 
     342             :         /// @brief Slice the container in the last dimension
     343             :         ///
     344             :         /// Access a portion of the container via a slice. The slicing always is in the last
     345             :         /// dimension. So for a 3D volume, the slice would be an sliced in the z direction and would
     346             :         /// be a part of the x-y plane.
     347             :         ///
     348             :         /// A slice is always the same dimension as the original DataContainer, but with a thickness
     349             :         /// of 1 in the last dimension (i.e. the coefficient of the last dimension is 1)
     350             :         const DataContainer<data_t> slice(index_t i) const;
     351             : 
     352             :         /// @brief Slice the container in the last dimension, non-const overload
     353             :         ///
     354             :         /// @overload
     355             :         /// @see slice(index_t) const
     356             :         DataContainer<data_t> slice(index_t i);
     357             : 
     358             :         /// returns iterator to the first element of the container
     359             :         iterator begin();
     360             : 
     361             :         /// returns const iterator to the first element of the container (cannot mutate data)
     362             :         const_iterator begin() const;
     363             : 
     364             :         /// returns const iterator to the first element of the container (cannot mutate data)
     365             :         const_iterator cbegin() const;
     366             : 
     367             :         /// returns iterator to one past the last element of the container
     368             :         iterator end();
     369             : 
     370             :         /// returns const iterator to one past the last element of the container (cannot mutate
     371             :         /// data)
     372             :         const_iterator end() const;
     373             : 
     374             :         /// returns const iterator to one past the last element of the container (cannot mutate
     375             :         /// data)
     376             :         const_iterator cend() const;
     377             : 
     378             :         /// value_type of the DataContainer elements for iterators
     379             :         using value_type = data_t;
     380             :         /// pointer type of DataContainer elements for iterators
     381             :         using pointer = data_t*;
     382             :         /// const pointer type of DataContainer elements for iterators
     383             :         using const_pointer = const data_t*;
     384             :         /// difference type for iterators
     385             :         using difference_type = std::ptrdiff_t;
     386             : 
     387             :         /// write a pretty-formatted string representation to stream
     388             :         void format(std::ostream& os, format_config cfg = {}) const;
     389             : 
     390             :     private:
     391             :         /// the current DataDescriptor
     392             :         std::unique_ptr<DataDescriptor> _dataDescriptor;
     393             : 
     394             :         /// the current DataHandler
     395             :         std::variant<ContiguousStorage<data_t>, ContiguousStorageView<data_t>> storage_;
     396             :     };
     397             : 
     398             :     /// pretty output formatting.
     399             :     /// for configurable output, use `DataContainerFormatter` directly.
     400             :     template <typename T>
     401             :     std::ostream& operator<<(std::ostream& os, const elsa::DataContainer<T>& dc)
     402           0 :     {
     403           0 :         dc.format(os);
     404           0 :         return os;
     405           0 :     }
     406             : 
     407             :     /// clip the container values outside of the interval, to the interval edges
     408             :     template <typename data_t>
     409             :     [[nodiscard]] DataContainer<data_t> clip(const DataContainer<data_t>& dc, data_t min,
     410             :                                              data_t max);
     411             : 
     412             :     /// Concatenate two DataContainers to one (requires copying of both)
     413             :     template <typename data_t>
     414             :     [[nodiscard]] DataContainer<data_t> concatenate(const DataContainer<data_t>& dc1,
     415             :                                                     const DataContainer<data_t>& dc2);
     416             : 
     417             :     /// Perform the (n-dimensional) FFT shift operation to the provided signal. Refer to
     418             :     /// https://numpy.org/doc/stable/reference/generated/numpy.fft.fftshift.html for further
     419             :     /// details.
     420             :     template <typename data_t>
     421             :     [[nodiscard]] DataContainer<data_t> fftShift(const DataContainer<data_t>& dc);
     422             : 
     423             :     /// Perform the (n-dimensional) IFFT shift operation to the provided signal. Refer to
     424             :     /// https://numpy.org/doc/stable/reference/generated/numpy.fft.ifftshift.html for further
     425             :     /// details.
     426             :     template <typename data_t>
     427             :     [[nodiscard]] DataContainer<data_t> ifftShift(const DataContainer<data_t>& dc);
     428             : 
     429             :     /// Perform the FFT shift operation to the provided signal. Refer to
     430             :     /// https://numpy.org/doc/stable/reference/generated/numpy.fft.fftshift.html for further
     431             :     /// details.
     432             :     template <typename data_t>
     433             :     [[nodiscard]] DataContainer<data_t> fftShift2D(const DataContainer<data_t>& dc);
     434             : 
     435             :     /// Perform the IFFT shift operation to the provided signal. Refer to
     436             :     /// https://numpy.org/doc/stable/reference/generated/numpy.fft.ifftshift.html for further
     437             :     /// details.
     438             :     template <typename data_t>
     439             :     [[nodiscard]] DataContainer<data_t> ifftShift2D(const DataContainer<data_t>& dc);
     440             : 
     441             :     /// Unary plus operator
     442             :     template <typename data_t>
     443             :     [[nodiscard]] inline DataContainer<data_t> operator+(const DataContainer<data_t>& x)
     444           7 :     {
     445           7 :         return x;
     446           7 :     }
     447             : 
     448             :     /// Unary negation operator
     449             :     template <typename data_t>
     450             :     [[nodiscard]] inline DataContainer<data_t> operator-(const DataContainer<data_t>& x)
     451        1887 :     {
     452        1887 :         return static_cast<data_t>(-1) * x;
     453        1887 :     }
     454             : 
     455             :     /// Multiplying two DataContainers
     456             :     template <typename data_t>
     457             :     [[nodiscard]] inline DataContainer<data_t> operator*(const DataContainer<data_t>& lhs,
     458             :                                                          const DataContainer<data_t>& rhs)
     459       10237 :     {
     460       10237 :         DataContainer<data_t> copy(lhs.getDataDescriptor());
     461       10237 :         copy.assign(lhs);
     462       10237 :         copy *= rhs;
     463       10237 :         return copy;
     464       10237 :     }
     465             : 
     466             :     template <typename data_t, typename Scalar,
     467             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     468             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     469             :     [[nodiscard]] inline DataContainer<data_t> operator*(const DataContainer<data_t>& dc,
     470             :                                                          const Scalar& s)
     471         357 :     {
     472         357 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     473         357 :         copy.assign(dc);
     474         357 :         copy *= static_cast<data_t>(s);
     475         357 :         return copy;
     476         357 :     }
     477             : 
     478             :     template <typename data_t, typename Scalar,
     479             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     480             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     481             :     [[nodiscard]] inline DataContainer<data_t> operator*(const Scalar& s,
     482             :                                                          const DataContainer<data_t>& dc)
     483        5062 :     {
     484        5062 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     485        5062 :         copy.assign(dc);
     486        5062 :         copy *= static_cast<data_t>(s);
     487        5062 :         return copy;
     488        5062 :     }
     489             : 
     490             :     /// Add two DataContainers
     491             :     template <typename data_t>
     492             :     [[nodiscard]] inline DataContainer<data_t> operator+(const DataContainer<data_t>& lhs,
     493             :                                                          const DataContainer<data_t>& rhs)
     494        1455 :     {
     495        1455 :         DataContainer<data_t> copy(lhs.getDataDescriptor());
     496        1455 :         copy.assign(lhs);
     497        1455 :         copy += rhs;
     498        1455 :         return copy;
     499        1455 :     }
     500             : 
     501             :     template <typename data_t, typename Scalar,
     502             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     503             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     504             :     [[nodiscard]] inline DataContainer<data_t> operator+(const DataContainer<data_t>& dc,
     505             :                                                          const Scalar& s)
     506           7 :     {
     507           7 :         DataContainer<data_t> copy(dc.getDataDescriptor());
     508           7 :         copy.assign(dc);
     509           7 :         copy += static_cast<data_t>(s);
     510           7 :         return copy;
     511           7 :     }
     512             : 
     513             :     template <typename data_t, typename Scalar,
     514             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     515             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     516             :     [[nodiscard]] inline DataContainer<data_t> operator+(const Scalar& s,
     517             :                                                          const DataContainer<data_t>& dc)
     518           9 :     {
     519           9 :         auto copy = DataContainer<data_t>(dc.getDataDescriptor());
     520           9 :         copy.assign(dc);
     521           9 :         copy += static_cast<data_t>(s);
     522           9 :         return copy;
     523           9 :     }
     524             : 
     525             :     /// Subtract two DataContainers
     526             :     template <typename data_t>
     527             :     [[nodiscard]] DataContainer<data_t> operator-(const DataContainer<data_t>& lhs,
     528             :                                                   const DataContainer<data_t>& rhs);
     529             : 
     530             :     template <typename data_t, typename Scalar,
     531             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     532             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     533             :     [[nodiscard]] DataContainer<std::common_type_t<data_t, Scalar>>
     534             :         operator-(const DataContainer<data_t>& dc, const Scalar& s);
     535             : 
     536             :     template <typename Scalar, typename data_t,
     537             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     538             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     539             :     [[nodiscard]] DataContainer<std::common_type_t<Scalar, data_t>>
     540             :         operator-(const Scalar& s, const DataContainer<data_t>& dc);
     541             : 
     542             :     /// Divide two DataContainers
     543             :     template <typename data_t>
     544             :     [[nodiscard]] DataContainer<data_t> operator/(const DataContainer<data_t>& lhs,
     545             :                                                   const DataContainer<data_t>& rhs);
     546             : 
     547             :     /// Divide DataContainer by scalar
     548             :     template <typename data_t, typename Scalar,
     549             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     550             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     551             :     [[nodiscard]] DataContainer<std::common_type_t<data_t, Scalar>>
     552             :         operator/(const DataContainer<data_t>& dc, const Scalar& s);
     553             : 
     554             :     /// Divide scalar with DataContainer
     555             :     template <typename Scalar, typename data_t,
     556             :               typename = std::enable_if_t<std::is_arithmetic_v<GetFloatingPointType_t<
     557             :                                               Scalar>> && std::is_convertible_v<Scalar, data_t>>>
     558             :     [[nodiscard]] DataContainer<std::common_type_t<Scalar, data_t>>
     559             :         operator/(const Scalar& s, const DataContainer<data_t>& dc);
     560             : 
     561             :     template <typename xdata_t, typename ydata_t>
     562             :     [[nodiscard]] DataContainer<value_type_of_t<std::common_type_t<xdata_t, ydata_t>>>
     563             :         cwiseMax(const DataContainer<xdata_t>& lhs, const DataContainer<ydata_t>& rhs);
     564             : 
     565             :     template <typename xdata_t, typename ydata_t>
     566             :     [[nodiscard]] DataContainer<value_type_of_t<std::common_type_t<xdata_t, ydata_t>>>
     567             :         cwiseMin(const DataContainer<xdata_t>& lhs, const DataContainer<ydata_t>& rhs);
     568             : 
     569             :     /// @brief Compute a coefficient wise square for each element of the `DataContainer`
     570             :     template <typename data_t>
     571             :     [[nodiscard]] DataContainer<data_t> square(const DataContainer<data_t>& dc);
     572             : 
     573             :     /// @brief Short convenience name for `square(dc)`.
     574             :     template <typename data_t>
     575             :     [[nodiscard]] DataContainer<data_t> sq(const DataContainer<data_t>& dc);
     576             : 
     577             :     /// @brief Compute a coefficient wise square root for each element of the `DataContainer`
     578             :     template <typename data_t>
     579             :     [[nodiscard]] DataContainer<data_t> sqrt(const DataContainer<data_t>& dc);
     580             : 
     581             :     /// @brief Compute a coefficient wise exponential for each element of the `DataContainer`
     582             :     template <typename data_t>
     583             :     [[nodiscard]] DataContainer<data_t> exp(const DataContainer<data_t>& dc);
     584             : 
     585             :     /// @brief Compute a coefficient wise log for each element of the `DataContainer`
     586             :     template <typename data_t>
     587             :     [[nodiscard]] DataContainer<data_t> log(const DataContainer<data_t>& dc);
     588             : 
     589             :     /// @brief Compute an element-wise log of modified bessel function of the first kind of
     590             :     /// order 0 for each element of the `DataContainer`
     591             :     template <typename data_t>
     592             :     DataContainer<data_t> bessel_log_0(const DataContainer<data_t>& dc);
     593             : 
     594             :     /// @brief Compute an element-wise modified bessel function of the first kind of order 1
     595             :     /// divided by that of the order 0 for each element of the `DataContainer`
     596             :     template <typename data_t>
     597             :     DataContainer<data_t> bessel_1_0(const DataContainer<data_t>& dc);
     598             : 
     599             :     /// @brief Compute a coefficient wise minimum with a scalar.
     600             :     /// For each element in `x_i` the given `DataContainer`, compute
     601             :     /// `min(x_i, scalar)`
     602             :     template <typename data_t>
     603             :     [[nodiscard]] DataContainer<data_t> minimum(const DataContainer<data_t>& dc,
     604             :                                                 SelfType_t<data_t> scalar);
     605             : 
     606             :     /// @brief Compute a coefficient wise maximum with a scalar.
     607             :     /// For each element in `x_i` the given `DataContainer`, compute
     608             :     /// `max(x_i, scalar)`
     609             :     template <typename data_t>
     610             :     [[nodiscard]] DataContainer<data_t> maximum(const DataContainer<data_t>& dc,
     611             :                                                 SelfType_t<data_t> scalar);
     612             : 
     613             :     /// @brief Return an owning DataContainer, if given an non-owning one, the data is copied to a
     614             :     /// new owning buffer.
     615             :     template <class data_t>
     616             :     [[nodiscard]] DataContainer<data_t> materialize(const DataContainer<data_t>& x);
     617             : 
     618             :     /// @brief Compute the absolute value for each of the coefficients of the given DataContainer
     619             :     template <typename data_t>
     620             :     [[nodiscard]] DataContainer<value_type_of_t<data_t>> cwiseAbs(const DataContainer<data_t>& dc);
     621             : 
     622             :     /**
     623             :      * @brief compute the sign of each entry of the input DataContainer. The
     624             :      * function is defined as:
     625             :      * \f[
     626             :      * \operatorname{sign}(x_i) =
     627             :      * \begin{cases}
     628             :      * 1 & \text{if } x_i > 0 \\
     629             :      * -1 & \text{if } x_i < 0 \\
     630             :      * 0 & \text{elsa}
     631             :      * \end{cases} \quad \forall i
     632             :      * \f]
     633             :      * For complex numbers, the definition is givens as:
     634             :      * \f[
     635             :      * \operatorname{sign}(x_i) =
     636             :      * \begin{cases}
     637             :      * 1 & \text{if } \mathrm{Re}(x_i) > 0 \\
     638             :      * -1 & \text{if } \mathrm{Re}(x_i) < 0 \\
     639             :      * sign(\mathrm{Im}(x_i)) & \text{elsa}
     640             :      * \end{cases} \quad \forall i
     641             :      * \f]
     642             :      */
     643             :     template <typename data_t>
     644             :     [[nodiscard]] DataContainer<value_type_of_t<data_t>> sign(const DataContainer<data_t>& dc);
     645             : 
     646             :     /// @brief Return a DataContainer with complex data type. If the input vector is real, all
     647             :     /// coefficients will be converted to complex values with a zero imaginary part. If the input is
     648             :     /// complex already, a copy of the container is returned
     649             :     template <typename data_t>
     650             :     [[nodiscard]] DataContainer<add_complex_t<data_t>> asComplex(const DataContainer<data_t>& dc);
     651             : 
     652             :     /// @brief Return the real part a complex DataContainer. If the input DataContainer is real,
     653             :     /// it is assumed that the imaginary part is zero and a copy is returned
     654             :     template <typename data_t>
     655             :     [[nodiscard]] DataContainer<value_type_of_t<data_t>> real(const DataContainer<data_t>& dc);
     656             : 
     657             :     /// @brief Return the imaginary part a complex DataContainer. If the input DataContainer is
     658             :     /// real, it is assumed that the imaginary part is zero and zero initialized DataContainer of
     659             :     /// the same size and DataDescriptor is returned
     660             :     template <typename data_t>
     661             :     [[nodiscard]] DataContainer<value_type_of_t<data_t>> imag(const DataContainer<data_t>& dc);
     662             : 
     663             :     /// Compute the linear combination of \f$a * x + b * y\f$.
     664             :     ///
     665             :     /// This function can be used as a memory efficient version for the computation
     666             :     /// of the above expression, as for such an expression (without expression template)
     667             :     /// multiple copies need to be created and allocated.
     668             :     ///
     669             :     /// The function throws, if x and y do not have the same data descriptor
     670             :     template <class data_t>
     671             :     [[nodiscard]] DataContainer<data_t>
     672             :         lincomb(SelfType_t<data_t> a, const DataContainer<data_t>& x, SelfType_t<data_t> b,
     673             :                 const DataContainer<data_t>& y);
     674             : 
     675             :     /// Compute the linear combination of \f$a * x + b * y\f$, and write it to
     676             :     /// the output variable.
     677             :     ///
     678             :     /// This function can be used as a memory efficient version for the computation
     679             :     /// of the above expression, as for such an expression (without expression template)
     680             :     /// multiple copies need to be created and allocated.
     681             :     ///
     682             :     /// The function throws, if x, y and out do not have the same data descriptor
     683             :     template <class data_t>
     684             :     void lincomb(SelfType_t<data_t> alpha, const DataContainer<data_t>& x, SelfType_t<data_t> b,
     685             :                  const DataContainer<data_t>& y, DataContainer<data_t>& out);
     686             : 
     687             :     /// Create a DataContainer filled with zeros and the given DataDescriptor
     688             :     template <class data_t>
     689             :     [[nodiscard]] DataContainer<data_t> zeros(const DataDescriptor& desc);
     690             : 
     691             :     /// Create a DataContainer filled with zeros and the DataDescriptor of the given DataContainer
     692             :     template <class data_t>
     693             :     [[nodiscard]] DataContainer<data_t> zeroslike(const DataContainer<data_t>& dc);
     694             : 
     695             :     /// Create a DataContainer filled with ones values and the given DataDescriptor
     696             :     template <class data_t>
     697             :     [[nodiscard]] DataContainer<data_t> ones(const DataDescriptor& desc);
     698             : 
     699             :     /// Create a DataContainer filled with ones and the DataDescriptor of the given DataContainer
     700             :     template <class data_t>
     701             :     [[nodiscard]] DataContainer<data_t> oneslike(const DataContainer<data_t>& dc);
     702             : 
     703             :     /// Create a DataContainer filled with the given value and DataDescriptor
     704             :     template <class data_t>
     705             :     [[nodiscard]] DataContainer<data_t> full(const DataDescriptor& desc, SelfType_t<data_t> value);
     706             : 
     707             :     /// Create a DataContainer filled with the given value and the DataDescriptor of the given
     708             :     /// DataContainer
     709             :     template <class data_t>
     710             :     [[nodiscard]] DataContainer<data_t> fulllike(const DataContainer<data_t>& dc,
     711             :                                                  SelfType_t<data_t> value);
     712             : 
     713             :     /// Create an uninitialized DataContainer, the caller is responsible to fill DataContainer
     714             :     /// before use
     715             :     template <class data_t>
     716             :     [[nodiscard]] DataContainer<data_t> empty(const DataDescriptor& desc);
     717             : 
     718             :     /// Create an uninitialized DataContainer with the DataDescriptor of the given DataContainer.
     719             :     /// The caller is responsible to fill DataContainer before use
     720             :     template <class data_t>
     721             :     [[nodiscard]] DataContainer<data_t> emptylike(const DataContainer<data_t>& dc);
     722             : 
     723             :     /// Perform a real-to-complex FFT on the given real DataContainer, normalizing with the given
     724             :     /// norm. This exploits the symmetrical spectrum of the FT of a real signal and computes only
     725             :     /// the complex halfspace. This means that the last dimension will have length floor(N/2) + 1,
     726             :     /// given the last dimension of the input is N. For further reference, see numpy, FFTW, Eigen or
     727             :     /// cuFFT documentation. Note that the input need not be padded to the output size.
     728             :     /// @param policy controls the choice of implementation. If the requested policy
     729             :     /// cannot be applied, a runtime error is generated.
     730             :     /// @see FFTPolicy
     731             :     template <typename data_t>
     732             :     [[nodiscard]] DataContainer<complex<data_t>> rfft(const DataContainer<data_t>& dc, FFTNorm norm,
     733             :                                                       FFTPolicy policy = FFTPolicy::AUTO);
     734             : 
     735             :     /// Perform a complex-to-real FFT on the given complex DataContainer, normalizing with the given
     736             :     /// norm. This exploits the symmetrical spectrum of the FT of a real signal and assumes that the
     737             :     /// complex halfspace is given as input. The input must be laid out as returned from rfft()!
     738             :     /// Note that, due to information lost in the floor(N/2) operation, the dimensions of
     739             :     /// dc.rfft().irfft() may not match the dimensions of dc. For further reference, see numpy,
     740             :     /// FFTW, Eigen or cuFFT documentation. Note that the output is not padded to the input size.
     741             :     /// @param policy controls the choice of implementation. If the requested policy
     742             :     /// cannot be applied, a runtime error is generated.
     743             :     /// @see FFTPolicy
     744             :     template <typename data_t>
     745             :     [[nodiscard]] DataContainer<data_t> irfft(const DataContainer<complex<data_t>>& dc,
     746             :                                               FFTNorm norm, FFTPolicy policy = FFTPolicy::AUTO);
     747             : 
     748             : } // namespace elsa

Generated by: LCOV version 1.14