LCOV - code coverage report
Current view: top level - variable/include/scipp/variable - bin_array_variable.tcc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 98 114 86.0 %
Date: 2024-04-28 01:25:40 Functions: 54 74 73.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: BSD-3-Clause
       2             : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
       3             : /// @file
       4             : /// @author Simon Heybrock
       5             : #include "scipp/variable/arithmetic.h"
       6             : #include "scipp/variable/bin_array_model.h"
       7             : #include "scipp/variable/cumulative.h"
       8             : #include "scipp/variable/shape.h"
       9             : #include "scipp/variable/structure_array_model.h"
      10             : #include "scipp/variable/variable.tcc"
      11             : #include "scipp/variable/variable_factory.h"
      12             : 
      13             : namespace scipp::variable {
      14             : 
      15             : // Avoid RTTI issues across DSO boundaries on OSX. Use of requireT and call of
      16             : // Variable::values<T>() causes extra instantiations and tests failing with
      17             : // std::bad_cast in dataset module. Helper functions have been moved to .cpp to
      18             : // avoid some of these (see namespace bin_array_variable_detail below, but this
      19             : // particular one is hard to avoid. The `extern template` declaration avoids the
      20             : // instantiation in a different DSO.
      21             : extern template class StructureArrayModel<scipp::index_pair, scipp::index>;
      22             : 
      23             : template <> struct model<scipp::index_pair> {
      24             :   using type = StructureArrayModel<scipp::index_pair, scipp::index>;
      25             : };
      26             : 
      27             : namespace bin_array_variable_detail {
      28             : SCIPP_VARIABLE_EXPORT std::tuple<Variable, scipp::index>
      29             : contiguous_indices(const Variable &parent, const Dimensions &dims);
      30             : SCIPP_VARIABLE_EXPORT const scipp::index_pair *
      31             : index_pair_data(const Variable &indices);
      32             : SCIPP_VARIABLE_EXPORT scipp::index size_from_end_index(const Variable &end);
      33             : SCIPP_VARIABLE_EXPORT VariableConceptHandle
      34             : zero_indices(const scipp::index size);
      35             : } // namespace bin_array_variable_detail
      36             : 
      37           1 : template <class T> std::tuple<Variable, Dim, T> Variable::to_constituents() {
      38           1 :   Variable tmp;
      39           1 :   std::swap(*this, tmp);
      40             :   // cppcheck-suppress constVariable # Deduced by auto.
      41           1 :   auto &model = requireT<BinArrayModel<T>>(tmp.data());
      42           2 :   return {tmp.bin_indices(), model.bin_dim(), std::move(model.buffer())};
      43           1 : }
      44             : 
      45      873479 : template <class T> std::tuple<Variable, Dim, T> Variable::constituents() const {
      46             :   // cppcheck-suppress constVariable # Deduced by auto.
      47      873479 :   auto &model = requireT<const BinArrayModel<T>>(data());
      48     1746956 :   return {bin_indices(), model.bin_dim(), model.buffer()};
      49             : }
      50             : 
      51       41589 : template <class T> std::tuple<Variable, Dim, T> Variable::constituents() {
      52             :   // cppcheck-suppress constVariable # Deduced by auto.
      53       41589 :   auto &model = requireT<BinArrayModel<T>>(data());
      54       83178 :   return {bin_indices(), model.bin_dim(), model.buffer()};
      55             : }
      56             : 
      57      119143 : template <class T> const T &Variable::bin_buffer() const {
      58      119143 :   return requireT<const BinArrayModel<T>>(data()).buffer();
      59             : }
      60             : 
      61        8459 : template <class T> T &Variable::bin_buffer() {
      62        8459 :   return requireT<BinArrayModel<T>>(data()).buffer();
      63             : }
      64             : 
      65             : template <class T> class BinVariableMakerCommon : public AbstractVariableMaker {
      66             : public:
      67      417676 :   [[nodiscard]] bool is_bins() const override { return true; }
      68        4476 :   [[nodiscard]] Variable empty_like(const Variable &prototype,
      69             :                                     const std::optional<Dimensions> &shape,
      70             :                                     const Variable &sizes) const override {
      71        4476 :     if (shape)
      72           0 :       throw except::TypeError(
      73             :           "Cannot specify shape in `empty_like` for prototype with bins, shape "
      74             :           "must be given by shape of `sizes`.");
      75        4476 :     const auto [indices, dim, buf] = prototype.constituents<T>();
      76        4476 :     auto sizes_ = sizes;
      77        4476 :     if (!sizes.is_valid()) {
      78        4443 :       const auto &[begin, end] = unzip(indices);
      79        4443 :       sizes_ = end - begin;
      80        4443 :     }
      81        4476 :     const auto end = cumsum(sizes_);
      82        4476 :     const auto begin = end - sizes_;
      83        4476 :     const auto size = bin_array_variable_detail::size_from_end_index(end);
      84             :     return make_bins_no_validate(zip(begin, end), dim,
      85        8952 :                                  resize_default_init(buf, dim, size));
      86        4476 :   }
      87             : };
      88             : 
      89             : template <class T> class BinVariableMaker : public BinVariableMakerCommon<T> {
      90             : private:
      91             :   const Variable &
      92       15429 :   bin_parent(const typename AbstractVariableMaker::parent_list &parents) const {
      93       33347 :     constexpr auto is_bins = [](const Variable &x) {
      94       33347 :       return x.dtype() == dtype<bucket<T>>;
      95             :     };
      96       15429 :     const auto count = std::count_if(parents.begin(), parents.end(), is_bins);
      97       15429 :     if (count == 0)
      98           0 :       throw except::BinnedDataError("Bin cannot have zero parents");
      99          44 :     if (!std::is_same_v<T, Variable> && (count > 1))
     100           1 :       throw except::BinnedDataError(
     101             :           "Binary operations such as '+' with binned data are only supported "
     102             :           "with dtype=VariableView, got dtype=" +
     103             :           to_string(dtype<bucket<T>>) +
     104             :           ". See "
     105             :           "https://scipp.github.io/user-guide/binned-data/"
     106             :           "computation.html#Event-centric-arithmetic for equivalent operations "
     107             :           "for binned (event) data.");
     108       15428 :     return *std::find_if(parents.begin(), parents.end(), is_bins);
     109             :   }
     110             :   virtual Variable call_make_bins(const Variable &parent,
     111             :                                   const Variable &indices, const Dim dim,
     112             :                                   const DType type, const Dimensions &dims,
     113             :                                   const units::Unit &unit,
     114             :                                   const bool variances) const = 0;
     115             : 
     116             : protected:
     117       95548 :   const T &buffer(const Variable &var) const {
     118       95548 :     return requireT<const BinArrayModel<T>>(var.data()).buffer();
     119             :   }
     120             :   // cppcheck-suppress constParameter # Overloading on const-ness.
     121       87006 :   T buffer(Variable &var) const {
     122       87006 :     return requireT<BinArrayModel<T>>(var.data()).buffer();
     123             :   }
     124             : 
     125             : public:
     126       15429 :   Variable create(const DType elem_dtype, const Dimensions &dims,
     127             :                   const units::Unit &unit, const bool variances,
     128             :                   const typename AbstractVariableMaker::parent_list &parents)
     129             :       const override {
     130       15429 :     const Variable &parent = bin_parent(parents);
     131       15428 :     const auto &[parentIndices, dim, buffer] = parent.constituents<T>();
     132       15428 :     auto [indices, size] =
     133             :         bin_array_variable_detail::contiguous_indices(parentIndices, dims);
     134       15428 :     auto bufferDims = buffer.dims();
     135       15428 :     bufferDims.resize(dim, size);
     136       15428 :     return call_make_bins(parent, indices, dim, elem_dtype, bufferDims, unit,
     137       30856 :                           variances);
     138       15428 :   }
     139             : 
     140          12 :   Dim elem_dim(const Variable &var) const override {
     141          12 :     return std::get<1>(var.constituents<T>());
     142             :   }
     143      136837 :   DType elem_dtype(const Variable &var) const override {
     144      136837 :     return std::get<2>(var.constituents<T>()).dtype();
     145             :   }
     146      126520 :   units::Unit elem_unit(const Variable &var) const override {
     147      126520 :     return std::get<2>(var.constituents<T>()).unit();
     148             :   }
     149       38221 :   void expect_can_set_elem_unit(const Variable &var,
     150             :                                 const units::Unit &u) const override {
     151       38221 :     if (elem_unit(var) != u && var.is_slice())
     152           6 :       throw except::UnitError("Partial view on data of variable cannot be "
     153             :                               "used to change the unit.");
     154       38215 :   }
     155       38196 :   void set_elem_unit(Variable &var, const units::Unit &u) const override {
     156       38196 :     std::get<2>(var.constituents<T>()).setUnit(u);
     157       38196 :   }
     158           5 :   bool has_masks(const Variable &var) const override {
     159             :     static_cast<void>(var);
     160             :     if constexpr (std::is_same_v<T, Variable>)
     161           0 :       return false;
     162             :     else
     163           5 :       return !std::get<2>(var.constituents<T>()).masks().empty();
     164             :   }
     165      154825 :   bool has_variances(const Variable &var) const override {
     166      154825 :     return std::get<2>(var.constituents<T>()).has_variances();
     167             :   }
     168             :   core::ElementArrayViewParams
     169      182511 :   array_params(const Variable &var) const override {
     170      182511 :     const auto &[indices, dim, buffer] = var.constituents<T>();
     171      182511 :     auto params = var.array_params();
     172             :     return {0, // no offset required in buffer since access via indices
     173             :             params.dims(),
     174             :             params.strides(),
     175      182511 :             {dim, buffer.dims(), Strides{buffer.strides()},
     176      547533 :              bin_array_variable_detail ::index_pair_data(indices)}};
     177      182511 :   }
     178             : };
     179             : 
     180           1 : template <class T> BinArrayModel<T> copy(const BinArrayModel<T> &model) {
     181           1 :   return BinArrayModel<T>(model.indices()->clone(), model.bin_dim(),
     182           2 :                           copy(model.buffer()));
     183             : }
     184             : 
     185             : template <class T>
     186      159193 : BinArrayModel<T>::BinArrayModel(const VariableConceptHandle &indices,
     187             :                                 const Dim dim, T buffer)
     188      159193 :     : BinModelBase<Indices>(indices, dim), m_buffer(std::move(buffer)) {}
     189             : 
     190           0 : template <class T> VariableConceptHandle BinArrayModel<T>::clone() const {
     191           0 :   return std::make_shared<BinArrayModel<T>>(variable::copy(*this));
     192             : }
     193             : 
     194             : template <class T>
     195           7 : bool BinArrayModel<T>::operator==(const BinArrayModel &other) const noexcept {
     196             :   using IndexModel = StructureArrayModel<scipp::index_pair, scipp::index>;
     197          14 :   if (indices()->dtype() != core::dtype<scipp::index_pair> ||
     198          14 :       other.indices()->dtype() != core::dtype<scipp::index_pair>)
     199           0 :     return false;
     200           7 :   const auto &i1 = requireT<const IndexModel>(*indices());
     201           7 :   const auto &i2 = requireT<const IndexModel>(*other.indices());
     202          12 :   return equals_impl(i1.values(), i2.values()) &&
     203          12 :          this->bin_dim() == other.bin_dim() && m_buffer == other.m_buffer;
     204             : }
     205             : 
     206             : template <class T>
     207             : VariableConceptHandle
     208           4 : BinArrayModel<T>::makeDefaultFromParent(const scipp::index size) const {
     209           8 :   return std::make_shared<BinArrayModel>(
     210           4 :       bin_array_variable_detail::zero_indices(size), this->bin_dim(),
     211           8 :       T{m_buffer.slice({this->bin_dim(), 0, 0})});
     212             : }
     213             : 
     214             : template <class T>
     215             : VariableConceptHandle
     216           0 : BinArrayModel<T>::makeDefaultFromParent(const Variable &shape) const {
     217           0 :   const auto end = cumsum(shape);
     218           0 :   const auto begin = end - shape;
     219           0 :   const auto size = bin_array_variable_detail::size_from_end_index(end);
     220             :   return std::make_shared<BinArrayModel>(
     221           0 :       zip(begin, begin).data_handle(), this->bin_dim(),
     222           0 :       resize_default_init(m_buffer, this->bin_dim(), size));
     223           0 : }
     224             : 
     225           0 : template <class T> void BinArrayModel<T>::assign(const VariableConcept &other) {
     226           0 :   *this = requireT<const BinArrayModel<T>>(other);
     227           0 : }
     228             : 
     229             : template <class T>
     230             : ElementArrayView<const scipp::index_pair>
     231       61943 : BinArrayModel<T>::index_values(const core::ElementArrayViewParams &base) const {
     232       61943 :   return requireT<const StructureArrayModel<scipp::index_pair, scipp::index>>(
     233       61943 :              *this->indices())
     234       61943 :       .values(base);
     235             : }
     236             : 
     237             : template <class T>
     238      159168 : Variable make_bins_impl(Variable indices, const Dim dim, T &&buffer) {
     239      159168 :   indices.setDataHandle(std::make_unique<variable::BinArrayModel<T>>(
     240      159168 :       indices.data_handle(), dim, std::move(buffer)));
     241      159168 :   return indices;
     242             : }
     243             : 
     244             : /// Macro for instantiating classes and functions required for support a new
     245             : /// bin dtype in Variable.
     246             : #define INSTANTIATE_BIN_ARRAY_VARIABLE(name, ...)                              \
     247             :   template <> struct model<core::bin<__VA_ARGS__>> {                           \
     248             :     using type = BinArrayModel<__VA_ARGS__>;                                   \
     249             :   };                                                                           \
     250             :   template SCIPP_EXPORT BinArrayModel<__VA_ARGS__> copy(                       \
     251             :       const BinArrayModel<__VA_ARGS__> &);                                     \
     252             :   template SCIPP_EXPORT Variable make_bins_impl(Variable, const Dim,           \
     253             :                                                 __VA_ARGS__ &&);               \
     254             :   template class SCIPP_EXPORT BinArrayModel<__VA_ARGS__>;                      \
     255             :   INSTANTIATE_VARIABLE_BASE(name, core::bin<__VA_ARGS__>)                      \
     256             :   template SCIPP_EXPORT std::tuple<Variable, Dim, __VA_ARGS__>                 \
     257             :   Variable::constituents<__VA_ARGS__>() const;                                 \
     258             :   template SCIPP_EXPORT const __VA_ARGS__ &Variable::bin_buffer<__VA_ARGS__>() \
     259             :       const;                                                                   \
     260             :   template SCIPP_EXPORT __VA_ARGS__ &Variable::bin_buffer<__VA_ARGS__>();      \
     261             :   template SCIPP_EXPORT std::tuple<Variable, Dim, __VA_ARGS__>                 \
     262             :   Variable::constituents<__VA_ARGS__>();                                       \
     263             :   template SCIPP_EXPORT std::tuple<Variable, Dim, __VA_ARGS__>                 \
     264             :   Variable::to_constituents<__VA_ARGS__>();
     265             : 
     266             : } // namespace scipp::variable

Generated by: LCOV version 1.14