LCOV - code coverage report
Current view: top level - elsa/core/Handlers - DataHandlerCPU.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 280 329 85.1 %
Date: 2022-08-25 03:05:39 Functions: 203 224 90.6 %

          Line data    Source code
       1             : #include "DataHandlerCPU.h"
       2             : #include "DataHandlerMapCPU.h"
       3             : #include "Error.h"
       4             : #include "TypeCasts.hpp"
       5             : 
       6             : #include "DataDescriptor.h"
       7             : 
       8             : #include <iostream>
       9             : 
      10             : #if WITH_FFTW
      11             : #define EIGEN_FFTW_DEFAULT
      12             : #endif
      13             : #include <unsupported/Eigen/FFT>
      14             : 
      15             : namespace elsa
      16             : {
      17             : 
      18             :     template <typename data_t>
      19             :     DataHandlerCPU<data_t>::DataHandlerCPU(index_t size)
      20             :         : _data(std::make_shared<DataVector_t>(size))
      21       65708 :     {
      22       65708 :     }
      23             : 
      24             :     template <typename data_t>
      25             :     DataHandlerCPU<data_t>::DataHandlerCPU(DataVector_t const& vector)
      26             :         : _data{std::make_shared<DataVector_t>(vector)}
      27         691 :     {
      28         691 :     }
      29             : 
      30             :     template <typename data_t>
      31             :     DataHandlerCPU<data_t>::DataHandlerCPU(const DataHandlerCPU<data_t>& other)
      32             :         : _data{other._data}, _associatedMaps{}
      33       15791 :     {
      34       15791 :     }
      35             : 
      36             :     template <typename data_t>
      37             :     DataHandlerCPU<data_t>::DataHandlerCPU(DataHandlerCPU<data_t>&& other)
      38             :         : _data{std::move(other._data)}, _associatedMaps{std::move(other._associatedMaps)}
      39           5 :     {
      40           5 :         for (auto& map : _associatedMaps)
      41           5 :             map->_dataOwner = this;
      42           5 :     }
      43             : 
      44             :     template <typename data_t>
      45             :     DataHandlerCPU<data_t>::~DataHandlerCPU()
      46       82195 :     {
      47       82195 :         for (auto& map : _associatedMaps)
      48           5 :             map->_dataOwner = nullptr;
      49       82195 :     }
      50             : 
      51             :     template <typename data_t>
      52             :     index_t DataHandlerCPU<data_t>::getSize() const
      53    22160971 :     {
      54    22160971 :         return static_cast<index_t>(_data->size());
      55    22160971 :     }
      56             : 
      57             :     template <typename data_t>
      58             :     data_t& DataHandlerCPU<data_t>::operator[](index_t index)
      59    13537746 :     {
      60    13537746 :         detach();
      61    13537746 :         return (*_data)[index];
      62    13537746 :     }
      63             : 
      64             :     template <typename data_t>
      65             :     const data_t& DataHandlerCPU<data_t>::operator[](index_t index) const
      66     4186559 :     {
      67     4186559 :         return (*_data)[index];
      68     4186559 :     }
      69             : 
      70             :     template <typename data_t>
      71             :     data_t DataHandlerCPU<data_t>::dot(const DataHandler<data_t>& v) const
      72         641 :     {
      73         641 :         if (v.getSize() != getSize())
      74           5 :             throw InvalidArgumentError("DataHandlerCPU: dot product argument has wrong size");
      75             : 
      76             :         // use Eigen if the other handler is CPU or Map, otherwise use the slow fallback version
      77         636 :         if (auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&v)) {
      78         631 :             return _data->dot(*otherHandler->_data);
      79         631 :         } else if (auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&v)) {
      80           5 :             return _data->dot(otherHandler->_map);
      81           5 :         } else {
      82           0 :             return this->slowDotProduct(v);
      83           0 :         }
      84         636 :     }
      85             : 
      86             :     template <typename data_t>
      87             :     GetFloatingPointType_t<data_t> DataHandlerCPU<data_t>::squaredL2Norm() const
      88       21533 :     {
      89       21533 :         return _data->squaredNorm();
      90       21533 :     }
      91             : 
      92             :     template <typename data_t>
      93             :     GetFloatingPointType_t<data_t> DataHandlerCPU<data_t>::l2Norm() const
      94        1773 :     {
      95        1773 :         return _data->norm();
      96        1773 :     }
      97             : 
      98             :     template <typename data_t>
      99             :     index_t DataHandlerCPU<data_t>::l0PseudoNorm() const
     100          14 :     {
     101          14 :         using FloatType = GetFloatingPointType_t<data_t>;
     102          14 :         return (_data->array().cwiseAbs() >= std::numeric_limits<FloatType>::epsilon()).count();
     103          14 :     }
     104             : 
     105             :     template <typename data_t>
     106             :     GetFloatingPointType_t<data_t> DataHandlerCPU<data_t>::l1Norm() const
     107          18 :     {
     108          18 :         return _data->array().abs().sum();
     109          18 :     }
     110             : 
     111             :     template <typename data_t>
     112             :     GetFloatingPointType_t<data_t> DataHandlerCPU<data_t>::lInfNorm() const
     113          18 :     {
     114          18 :         return _data->array().abs().maxCoeff();
     115          18 :     }
     116             : 
     117             :     template <typename data_t>
     118             :     data_t DataHandlerCPU<data_t>::sum() const
     119          10 :     {
     120          10 :         return _data->sum();
     121          10 :     }
     122             : 
     123             :     template <typename data_t>
     124             :     data_t DataHandlerCPU<data_t>::minElement() const
     125          36 :     {
     126          36 :         if constexpr (isComplex<data_t>) {
     127          34 :             throw LogicError("DataHandlerCPU: minElement of complex type not supported");
     128          34 :         } else {
     129          34 :             return _data->minCoeff();
     130          34 :         }
     131          36 :     }
     132             : 
     133             :     template <typename data_t>
     134             :     data_t DataHandlerCPU<data_t>::maxElement() const
     135          14 :     {
     136          14 :         if constexpr (isComplex<data_t>) {
     137          12 :             throw LogicError("DataHandlerCPU: maxElement of complex type not supported");
     138          12 :         } else {
     139          12 :             return _data->maxCoeff();
     140          12 :         }
     141          14 :     }
     142             : 
     143             :     template <typename data_t>
     144             :     DataHandler<data_t>& DataHandlerCPU<data_t>::fft(const DataDescriptor& source_desc,
     145             :                                                      FFTNorm norm)
     146         146 :     {
     147         146 :         this->base_fft<true>(source_desc, norm);
     148         146 :         return *this;
     149         146 :     }
     150             : 
     151             :     template <typename data_t>
     152             :     DataHandler<data_t>& DataHandlerCPU<data_t>::ifft(const DataDescriptor& source_desc,
     153             :                                                       FFTNorm norm)
     154         250 :     {
     155         250 :         this->base_fft<false>(source_desc, norm);
     156         250 :         return *this;
     157         250 :     }
     158             : 
     159             :     template <typename data_t>
     160             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator+=(const DataHandler<data_t>& v)
     161        8347 :     {
     162        8347 :         if (v.getSize() != getSize())
     163           5 :             throw InvalidArgumentError("DataHandler: addition argument has wrong size");
     164             : 
     165        8342 :         detach();
     166             : 
     167             :         // use Eigen if the other handler is CPU or Map, otherwise use the slow fallback version
     168        8342 :         if (auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&v)) {
     169        8337 :             *_data += *otherHandler->_data;
     170        8337 :         } else if (auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&v)) {
     171           5 :             *_data += otherHandler->_map;
     172           5 :         } else {
     173           0 :             this->slowAddition(v);
     174           0 :         }
     175             : 
     176        8342 :         return *this;
     177        8342 :     }
     178             : 
     179             :     template <typename data_t>
     180             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator-=(const DataHandler<data_t>& v)
     181       11760 :     {
     182       11760 :         if (v.getSize() != getSize())
     183           5 :             throw InvalidArgumentError("DataHandler: subtraction argument has wrong size");
     184             : 
     185       11755 :         detach();
     186             : 
     187             :         // use Eigen if the other handler is CPU or Map, otherwise use the slow fallback version
     188       11755 :         if (auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&v)) {
     189       11750 :             *_data -= *otherHandler->_data;
     190       11750 :         } else if (auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&v)) {
     191           5 :             *_data -= otherHandler->_map;
     192           5 :         } else {
     193           0 :             this->slowSubtraction(v);
     194           0 :         }
     195             : 
     196       11755 :         return *this;
     197       11755 :     }
     198             : 
     199             :     template <typename data_t>
     200             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator*=(const DataHandler<data_t>& v)
     201          25 :     {
     202          25 :         if (v.getSize() != getSize())
     203           5 :             throw InvalidArgumentError("DataHandler: multiplication argument has wrong size");
     204             : 
     205          20 :         detach();
     206             : 
     207             :         // use Eigen if the other handler is CPU or Map, otherwise use the slow fallback version
     208          20 :         if (auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&v)) {
     209          15 :             _data->array() *= otherHandler->_data->array();
     210          15 :         } else if (auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&v)) {
     211           5 :             _data->array() *= otherHandler->_map.array();
     212           5 :         } else {
     213           0 :             this->slowMultiplication(v);
     214           0 :         }
     215             : 
     216          20 :         return *this;
     217          20 :     }
     218             : 
     219             :     template <typename data_t>
     220             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator/=(const DataHandler<data_t>& v)
     221          25 :     {
     222          25 :         if (v.getSize() != getSize())
     223           5 :             throw InvalidArgumentError("DataHandler: division argument has wrong size");
     224             : 
     225          20 :         detach();
     226             : 
     227             :         // use Eigen if the other handler is CPU or Map, otherwise use the slow fallback version
     228          20 :         if (auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&v)) {
     229          15 :             _data->array() /= otherHandler->_data->array();
     230          15 :         } else if (auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&v)) {
     231           5 :             _data->array() /= otherHandler->_map.array();
     232           5 :         } else {
     233           0 :             this->slowDivision(v);
     234           0 :         }
     235             : 
     236          20 :         return *this;
     237          20 :     }
     238             : 
     239             :     template <typename data_t>
     240             :     DataHandlerCPU<data_t>& DataHandlerCPU<data_t>::operator=(const DataHandlerCPU<data_t>& v)
     241          65 :     {
     242          65 :         if (v.getSize() != getSize())
     243           5 :             throw InvalidArgumentError("DataHandler: assignment argument has wrong size");
     244             : 
     245          60 :         attach(v._data);
     246          60 :         return *this;
     247          60 :     }
     248             : 
     249             :     template <typename data_t>
     250             :     DataHandlerCPU<data_t>& DataHandlerCPU<data_t>::operator=(DataHandlerCPU<data_t>&& v)
     251          15 :     {
     252          15 :         if (v.getSize() != getSize())
     253           5 :             throw InvalidArgumentError("DataHandler: assignment argument has wrong size");
     254             : 
     255          10 :         attach(std::move(v._data));
     256             : 
     257          10 :         for (auto& map : v._associatedMaps)
     258          10 :             map->_dataOwner = this;
     259             : 
     260          10 :         _associatedMaps.splice(_associatedMaps.end(), std::move(v._associatedMaps));
     261             : 
     262             :         // make sure v no longer owns the object
     263          10 :         v._data.reset();
     264          10 :         return *this;
     265          10 :     }
     266             : 
     267             :     template <typename data_t>
     268             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator+=(data_t scalar)
     269          15 :     {
     270          15 :         detach();
     271          15 :         _data->array() += scalar;
     272          15 :         return *this;
     273          15 :     }
     274             : 
     275             :     template <typename data_t>
     276             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator-=(data_t scalar)
     277          15 :     {
     278          15 :         detach();
     279          15 :         _data->array() -= scalar;
     280          15 :         return *this;
     281          15 :     }
     282             : 
     283             :     template <typename data_t>
     284             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator*=(data_t scalar)
     285         514 :     {
     286         514 :         detach();
     287         514 :         _data->array() *= scalar;
     288         514 :         return *this;
     289         514 :     }
     290             : 
     291             :     template <typename data_t>
     292             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator/=(data_t scalar)
     293          15 :     {
     294          15 :         detach();
     295          15 :         _data->array() /= scalar;
     296          15 :         return *this;
     297          15 :     }
     298             : 
     299             :     template <typename data_t>
     300             :     DataHandler<data_t>& DataHandlerCPU<data_t>::operator=(data_t scalar)
     301        3721 :     {
     302        3721 :         detach();
     303        3721 :         _data->setConstant(scalar);
     304        3721 :         return *this;
     305        3721 :     }
     306             : 
     307             :     template <typename data_t>
     308             :     std::unique_ptr<DataHandler<data_t>> DataHandlerCPU<data_t>::getBlock(index_t startIndex,
     309             :                                                                           index_t numberOfElements)
     310        3337 :     {
     311        3337 :         if (startIndex >= getSize() || numberOfElements > getSize() - startIndex)
     312          10 :             throw InvalidArgumentError("DataHandler: requested block out of bounds");
     313             : 
     314        3327 :         return std::unique_ptr<DataHandlerMapCPU<data_t>>{
     315        3327 :             new DataHandlerMapCPU{this, _data->data() + startIndex, numberOfElements}};
     316        3327 :     }
     317             : 
     318             :     template <typename data_t>
     319             :     std::unique_ptr<const DataHandler<data_t>>
     320             :         DataHandlerCPU<data_t>::getBlock(index_t startIndex, index_t numberOfElements) const
     321         210 :     {
     322         210 :         if (startIndex >= getSize() || numberOfElements > getSize() - startIndex)
     323           0 :             throw InvalidArgumentError("DataHandler: requested block out of bounds");
     324             : 
     325             :         // using a const_cast here is fine as long as the DataHandlers never expose the internal
     326             :         // Eigen objects
     327         210 :         auto mutableThis = const_cast<DataHandlerCPU<data_t>*>(this);
     328         210 :         auto mutableData = const_cast<data_t*>(_data->data() + startIndex);
     329         210 :         return std::unique_ptr<const DataHandlerMapCPU<data_t>>{
     330         210 :             new DataHandlerMapCPU{mutableThis, mutableData, numberOfElements}};
     331         210 :     }
     332             : 
     333             :     template <typename data_t>
     334             :     DataHandlerCPU<data_t>* DataHandlerCPU<data_t>::cloneImpl() const
     335       15621 :     {
     336       15621 :         return new DataHandlerCPU<data_t>(*this);
     337       15621 :     }
     338             : 
     339             :     template <typename data_t>
     340             :     bool DataHandlerCPU<data_t>::isEqual(const DataHandler<data_t>& other) const
     341        1907 :     {
     342        1907 :         if (const auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&other)) {
     343             : 
     344          65 :             if (_data->size() != otherHandler->_map.size())
     345           5 :                 return false;
     346             : 
     347          60 :             if (_data->data() != otherHandler->_map.data() && *_data != otherHandler->_map)
     348          10 :                 return false;
     349             : 
     350          50 :             return true;
     351        1842 :         } else if (const auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&other)) {
     352             : 
     353        1842 :             if (_data->size() != otherHandler->_data->size())
     354           5 :                 return false;
     355             : 
     356        1837 :             if (_data->data() != otherHandler->_data->data() && *_data != *otherHandler->_data)
     357          51 :                 return false;
     358             : 
     359        1786 :             return true;
     360        1786 :         } else
     361           0 :             return false;
     362        1907 :     }
     363             : 
     364             :     template <typename data_t>
     365             :     void DataHandlerCPU<data_t>::assign(const DataHandler<data_t>& other)
     366       17895 :     {
     367             : 
     368       17895 :         if (const auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&other)) {
     369          22 :             if (getSize() == otherHandler->_dataOwner->getSize()) {
     370          10 :                 attach(otherHandler->_dataOwner->_data);
     371          12 :             } else {
     372          12 :                 detachWithUninitializedBlock(0, getSize());
     373          12 :                 *_data = otherHandler->_map;
     374          12 :             }
     375       17873 :         } else if (const auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&other)) {
     376       17873 :             attach(otherHandler->_data);
     377       17873 :         } else
     378           0 :             this->slowAssign(other);
     379       17895 :     }
     380             : 
     381             :     template <typename data_t>
     382             :     void DataHandlerCPU<data_t>::assign(DataHandler<data_t>&& other)
     383        8004 :     {
     384        8004 :         if (const auto otherHandler = downcast_safe<DataHandlerMapCPU<data_t>>(&other)) {
     385          20 :             if (getSize() == otherHandler->_dataOwner->getSize()) {
     386          10 :                 attach(otherHandler->_dataOwner->_data);
     387          10 :             } else {
     388          10 :                 detachWithUninitializedBlock(0, getSize());
     389          10 :                 *_data = otherHandler->_map;
     390          10 :             }
     391        7984 :         } else if (const auto otherHandler = downcast_safe<DataHandlerCPU<data_t>>(&other)) {
     392        7984 :             attach(std::move(otherHandler->_data));
     393             : 
     394        7984 :             for (auto& map : otherHandler->_associatedMaps)
     395          10 :                 map->_dataOwner = this;
     396             : 
     397        7984 :             _associatedMaps.splice(_associatedMaps.end(), std::move(otherHandler->_associatedMaps));
     398             : 
     399             :             // make sure v no longer owns the object
     400        7984 :             otherHandler->_data.reset();
     401        7984 :         } else
     402           0 :             this->slowAssign(other);
     403        8004 :     }
     404             : 
     405             :     template <typename data_t>
     406             :     void DataHandlerCPU<data_t>::detach()
     407    14000381 :     {
     408    14000381 :         if (_data.use_count() != 1) {
     409       16933 : #pragma omp barrier
     410       16933 : #pragma omp single
     411       16933 :             {
     412       16933 :                 data_t* oldData = _data->data();
     413             : 
     414             :                 // create deep copy of vector
     415       16933 :                 _data = std::make_shared<DataVector_t>(*_data);
     416             : 
     417             :                 // modify all associated maps
     418       16933 :                 for (auto map : _associatedMaps)
     419       16933 :                     new (&map->_map) Eigen::Map<DataVector_t>(
     420       16933 :                         _data->data() + (map->_map.data() - oldData), map->getSize());
     421       16933 :             }
     422       16933 :         }
     423    14000381 :     }
     424             : 
     425             :     template <typename data_t>
     426             :     void DataHandlerCPU<data_t>::detachWithUninitializedBlock(index_t startIndex,
     427             :                                                               index_t numberOfElements)
     428         775 :     {
     429         775 :         if (_data.use_count() != 1) {
     430             :             // allocate new vector
     431          74 :             auto newData = std::make_shared<DataVector_t>(getSize());
     432             : 
     433             :             // copy elements before start of block
     434          74 :             newData->head(startIndex) = _data->head(startIndex);
     435          74 :             newData->tail(getSize() - startIndex - numberOfElements) =
     436          74 :                 _data->tail(getSize() - startIndex - numberOfElements);
     437             : 
     438             :             // modify all associated maps
     439          74 :             for (auto map : _associatedMaps)
     440          76 :                 new (&map->_map) Eigen::Map<DataVector_t>(
     441          76 :                     newData->data() + (map->_map.data() - _data->data()), map->getSize());
     442             : 
     443          74 :             _data = newData;
     444          74 :         }
     445         775 :     }
     446             : 
     447             :     template <typename data_t>
     448             :     void DataHandlerCPU<data_t>::attach(const std::shared_ptr<DataVector_t>& data)
     449       18049 :     {
     450       18049 :         data_t* oldData = _data->data();
     451             : 
     452             :         // shallow copy
     453       18049 :         _data = data;
     454             : 
     455             :         // modify all associated maps
     456       18049 :         for (auto& map : _associatedMaps)
     457         136 :             new (&map->_map) Eigen::Map<DataVector_t>(_data->data() + (map->_map.data() - oldData),
     458         136 :                                                       map->getSize());
     459       18049 :     }
     460             : 
     461             :     template <typename data_t>
     462             :     void DataHandlerCPU<data_t>::attach(std::shared_ptr<DataVector_t>&& data)
     463        7994 :     {
     464        7994 :         data_t* oldData = _data->data();
     465             : 
     466             :         // shallow copy
     467        7994 :         _data = std::move(data);
     468             : 
     469             :         // modify all associated maps
     470        7994 :         for (auto& map : _associatedMaps)
     471          20 :             new (&map->_map) Eigen::Map<DataVector_t>(_data->data() + (map->_map.data() - oldData),
     472          20 :                                                       map->getSize());
     473        7994 :     }
     474             : 
     475             :     template <typename data_t>
     476             :     typename DataHandlerCPU<data_t>::DataMap_t DataHandlerCPU<data_t>::accessData() const
     477           0 :     {
     478           0 :         return DataMap_t(&(_data->operator[](0)), getSize());
     479           0 :     }
     480             : 
     481             :     template <typename data_t>
     482             :     typename DataHandlerCPU<data_t>::DataMap_t DataHandlerCPU<data_t>::accessData()
     483      211024 :     {
     484      211024 :         detach();
     485      211024 :         return DataMap_t(&(_data->operator[](0)), getSize());
     486      211024 :     }
     487             : 
     488             :     template <typename data_t>
     489             :     template <bool is_forward>
     490             :     void DataHandlerCPU<data_t>::base_fft(const DataDescriptor& source_desc, FFTNorm norm)
     491         396 :     {
     492         396 :         if constexpr (isComplex<data_t>) {
     493             : 
     494             :             // now that we change this datahandler,
     495             :             // copy-on-write it.
     496           0 :             this->detach();
     497             : 
     498           0 :             const auto& src_shape = source_desc.getNumberOfCoefficientsPerDimension();
     499           0 :             const auto& src_dims = source_desc.getNumberOfDimensions();
     500             : 
     501           0 :             typename DataVector_t::Scalar* this_data = this->_data->data();
     502             : 
     503             :             // TODO: fftw variant
     504             : 
     505             :             // generalization of an 1D-FFT
     506             :             // walk over each dimenson and 1d-fft one 'line' of data
     507        1188 :             for (index_t dim_idx = 0; dim_idx < src_dims; ++dim_idx) {
     508             :                 // jumps in the data for the current dimension's data
     509             :                 // dim_size[0] * dim_size[1] * ...
     510             :                 // 1 for dim_idx == 0.
     511         792 :                 const index_t stride = src_shape.head(dim_idx).prod();
     512             : 
     513             :                 // number of coefficients for the current dimension
     514         792 :                 const index_t dim_size = src_shape(dim_idx);
     515             : 
     516             :                 // number of coefficients for the other dimensions
     517             :                 // this is the number of 1d-ffts we'll do
     518             :                 // e.g. shape=[2, 3, 4] and we do dim_idx=2 (=shape 4)
     519             :                 //   -> src_shape.prod() == 24 / 4 = 6 == 2*3
     520         792 :                 const index_t other_dims_size = src_shape.prod() / dim_size;
     521             : 
     522         792 : #ifndef EIGEN_FFTW_DEFAULT
     523             : // when using eigen+fftw, this corrupts the memory, so don't parallelize.
     524             : // error messages may include:
     525             : // * double free or corruption (fasttop)
     526             : // * malloc_consolidate(): unaligned fastbin chunk detected
     527         792 : #pragma omp parallel for
     528         792 : #endif
     529             :                 // do all the 1d-ffts along the current dimensions axis
     530         792 :                 for (index_t i = 0; i < other_dims_size; ++i) {
     531             : 
     532           0 :                     index_t ray_start = i;
     533             :                     // each time i is a multiple of stride,
     534             :                     // jump forward the current+previous dimensions' shape product
     535             :                     // (draw an indexed 3d cube to visualize this)
     536           0 :                     ray_start += (stride * (dim_size - 1)) * ((i - (i % stride)) / stride);
     537             : 
     538             :                     // this is one "ray" through the volume
     539           0 :                     Eigen::Map<DataVector_t, Eigen::AlignmentType::Unaligned, Eigen::InnerStride<>>
     540           0 :                         input_map(this_data + ray_start, dim_size, Eigen::InnerStride<>(stride));
     541             : 
     542           0 :                     using inner_t = GetFloatingPointType_t<typename DataVector_t::Scalar>;
     543             : 
     544           0 :                     Eigen::FFT<inner_t> fft_op;
     545             : 
     546             :                     // disable any scaling in eigen - normally it does 1/n for ifft
     547           0 :                     fft_op.SetFlag(Eigen::FFT<inner_t>::Flag::Unscaled);
     548             : 
     549           0 :                     Eigen::Matrix<std::complex<inner_t>, Eigen::Dynamic, 1> fft_in{dim_size};
     550           0 :                     Eigen::Matrix<std::complex<inner_t>, Eigen::Dynamic, 1> fft_out{dim_size};
     551             : 
     552             :                     // eigen internally copies the fwd input matrix anyway if
     553             :                     // it doesn't have stride == 1
     554           0 :                     fft_in =
     555           0 :                         input_map.block(0, 0, dim_size, 1).template cast<std::complex<inner_t>>();
     556             : 
     557           0 :                     if (unlikely(dim_size == 1)) {
     558             :                         // eigen kiss-fft crashes for size=1...
     559           0 :                         fft_out = fft_in;
     560           0 :                     } else {
     561             :                         // arguments for in and out _must not_ be the same matrix!
     562             :                         // they will corrupt wildly otherwise.
     563           0 :                         if constexpr (is_forward) {
     564           0 :                             fft_op.fwd(fft_out, fft_in);
     565       18068 :                             if (norm == FFTNorm::FORWARD) {
     566           0 :                                 fft_out /= dim_size;
     567       18068 :                             } else if (norm == FFTNorm::ORTHO) {
     568           0 :                                 fft_out /= std::sqrt(dim_size);
     569           0 :                             }
     570       18068 :                         } else {
     571           0 :                             fft_op.inv(fft_out, fft_in);
     572       26228 :                             if (norm == FFTNorm::BACKWARD) {
     573       26228 :                                 fft_out /= dim_size;
     574 >1844*10^16 :                             } else if (norm == FFTNorm::ORTHO) {
     575           0 :                                 fft_out /= std::sqrt(dim_size);
     576           0 :                             }
     577           0 :                         }
     578           0 :                     }
     579             : 
     580             :                     // we can't directly use the map as fft output,
     581             :                     // since Eigen internally just uses the pointer to
     582             :                     // the map's first element, and doesn't respect stride at all..
     583           0 :                     input_map.block(0, 0, dim_size, 1) = fft_out.template cast<data_t>();
     584           0 :                 }
     585         792 :             }
     586         396 :         } else {
     587           0 :             throw Error{"fft with non-complex input container not supported"};
     588           0 :         }
     589         396 :     }
     590             : 
     591             :     // ------------------------------------------
     592             :     // explicit template instantiation
     593             :     template class DataHandlerCPU<float>;
     594             :     template class DataHandlerCPU<complex<float>>;
     595             :     template class DataHandlerCPU<double>;
     596             :     template class DataHandlerCPU<complex<double>>;
     597             :     template class DataHandlerCPU<index_t>;
     598             : } // namespace elsa

Generated by: LCOV version 1.14