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 : #pragma once
6 :
7 : #include <optional>
8 : #include <string>
9 : #include <utility>
10 : #include <vector>
11 :
12 : #include "scipp-variable_export.h"
13 : #include "scipp/common/index.h"
14 : #include "scipp/units/unit.h"
15 :
16 : #include "scipp/core/dimensions.h"
17 : #include "scipp/core/dtype.h"
18 : #include "scipp/core/element_array.h"
19 : #include "scipp/core/element_array_view.h"
20 : #include "scipp/core/except.h"
21 : #include "scipp/core/slice.h"
22 : #include "scipp/core/strides.h"
23 :
24 : #include "scipp/variable/variable_keyword_arg_constructor.h"
25 :
26 : namespace llnl::units {
27 : class precise_measurement;
28 : }
29 :
30 : namespace scipp::variable {
31 :
32 : class VariableConcept;
33 : using VariableConceptHandle = std::shared_ptr<VariableConcept>;
34 :
35 : /// Return the default unit (dimensionless or none) for a given dtype.
36 : [[nodiscard]] SCIPP_VARIABLE_EXPORT units::Unit
37 : default_unit_for(const DType type);
38 :
39 : /// Variable is a type-erased handle to any data structure representing a
40 : /// multi-dimensional array. In addition it has a unit and a set of dimension
41 : /// labels.
42 : class SCIPP_VARIABLE_EXPORT Variable {
43 : public:
44 2084367 : Variable() = default;
45 : Variable(const Variable &parent, const Dimensions &dims);
46 : Variable(const Dimensions &dims, VariableConceptHandle data);
47 : template <class T>
48 : Variable(const std::optional<units::Unit> &unit, const Dimensions &dimensions,
49 : T values, std::optional<T> variances);
50 : explicit Variable(const llnl::units::precise_measurement &m);
51 :
52 : /// Keyword-argument constructor.
53 : ///
54 : /// This is equivalent to `makeVariable`, except that the dtype is passed at
55 : /// runtime as first argument instead of a template argument. `makeVariable`
56 : /// should be preferred where possible, since it generates less code.
57 : template <class... Ts> Variable(const DType &type, Ts &&...args);
58 :
59 5938019 : Variable(const Variable &other) = default;
60 4354465 : Variable(Variable &&other) noexcept = default;
61 :
62 : Variable &operator=(const Variable &other);
63 : Variable &operator=(Variable &&other);
64 :
65 14817439 : ~Variable() noexcept = default;
66 :
67 : [[nodiscard]] const units::Unit &unit() const;
68 : void setUnit(const units::Unit &unit);
69 : void expect_can_set_unit(const units::Unit &unit) const;
70 :
71 : [[nodiscard]] const Dimensions &dims() const;
72 : [[nodiscard]] Dim dim() const;
73 : [[nodiscard]] scipp::index ndim() const;
74 :
75 : [[nodiscard]] DType dtype() const;
76 :
77 : [[nodiscard]] scipp::span<const scipp::index> strides() const;
78 : [[nodiscard]] scipp::index stride(const Dim dim) const;
79 : [[nodiscard]] scipp::index offset() const;
80 :
81 : [[nodiscard]] bool has_variances() const;
82 :
83 : template <class T> ElementArrayView<const T> values() const;
84 : template <class T> ElementArrayView<T> values();
85 : template <class T> ElementArrayView<const T> variances() const;
86 : template <class T> ElementArrayView<T> variances();
87 20370 : template <class T> const auto &value() const {
88 20370 : core::expect::ndim_is(dims(), 0);
89 20368 : return values<T>()[0];
90 : }
91 : template <class T> const auto &variance() const {
92 : core::expect::ndim_is(dims(), 0);
93 : return variances<T>()[0];
94 : }
95 25791 : template <class T> auto &value() {
96 25791 : core::expect::ndim_is(dims(), 0);
97 25791 : return values<T>()[0];
98 : }
99 : template <class T> auto &variance() {
100 : core::expect::ndim_is(dims(), 0);
101 : return variances<T>()[0];
102 : }
103 :
104 : [[nodiscard]] Variable slice(Slice params) const;
105 : void validateSlice(const Slice &s, const Variable &data) const;
106 : [[maybe_unused]] Variable &setSlice(Slice params, const Variable &data);
107 :
108 : template <class T> Variable elements() const;
109 : template <class T> Variable elements(const std::string &key) const;
110 :
111 : [[nodiscard]] Variable
112 : rename_dims(const std::vector<std::pair<Dim, Dim>> &names,
113 : const bool fail_on_unknown = true) const;
114 :
115 : bool operator==(const Variable &other) const;
116 : bool operator!=(const Variable &other) const;
117 :
118 : [[nodiscard]] const VariableConcept &data() const && = delete;
119 : [[nodiscard]] const VariableConcept &data() const &;
120 : VariableConcept &data() && = delete;
121 : VariableConcept &data() &;
122 : [[nodiscard]] const VariableConceptHandle &data_handle() const;
123 : void setDataHandle(VariableConceptHandle object);
124 :
125 : void setVariances(const Variable &v);
126 :
127 : [[nodiscard]] core::ElementArrayViewParams array_params() const;
128 :
129 : [[nodiscard]] Variable bin_indices() const;
130 : template <class T> const T &bin_buffer() const;
131 : template <class T> T &bin_buffer();
132 :
133 : template <class T> std::tuple<Variable, Dim, T> constituents() const;
134 : template <class T> std::tuple<Variable, Dim, T> constituents();
135 : template <class T> std::tuple<Variable, Dim, T> to_constituents();
136 :
137 : [[nodiscard]] Variable broadcast(const Dimensions &target) const;
138 : [[nodiscard]] Variable fold(const Dim dim, const Dimensions &target) const;
139 : [[nodiscard]] Variable transpose(scipp::span<const Dim> order) const;
140 :
141 : [[nodiscard]] bool is_valid() const noexcept;
142 : [[nodiscard]] bool is_slice() const;
143 : [[nodiscard]] bool is_readonly() const noexcept;
144 : [[nodiscard]] bool is_same(const Variable &other) const noexcept;
145 :
146 : [[nodiscard]] bool is_aligned() const noexcept;
147 : void set_aligned(bool aligned) noexcept;
148 :
149 : [[nodiscard]] Variable as_const() const;
150 :
151 43569 : auto &unchecked_dims() { return m_dims; }
152 24493 : auto &unchecked_strides() { return m_strides; }
153 :
154 : private:
155 : // Declared friend so gtest recognizes it
156 : friend SCIPP_VARIABLE_EXPORT std::ostream &operator<<(std::ostream &,
157 : const Variable &);
158 : template <class... Ts, class... Args>
159 : static Variable construct(const DType &type, Args &&...args);
160 : template <class T, class... Index>
161 : Variable elements_impl(Index... index) const;
162 :
163 : void expect_writable() const;
164 :
165 : Dimensions m_dims;
166 : Strides m_strides;
167 : scipp::index m_offset{0};
168 : VariableConceptHandle m_object;
169 : bool m_readonly{false};
170 : bool m_aligned{true};
171 : };
172 :
173 : /// Factory function for Variable supporting "keyword arguments"
174 : ///
175 : /// Two styles are supported:
176 : /// makeVariable<ElementType>(Dims, Shape, Unit, Values<T1>, Variances<T2>)
177 : /// or
178 : /// makeVariable<ElementType>(Dimensions, Unit, Values<T1>, Variances<T2>)
179 : /// Unit, Values, or Variances can be omitted. The order of arguments is
180 : /// arbitrary.
181 : /// Example:
182 : /// makeVariable<float>(units::kg,
183 : /// Shape{1, 2}, Dims{Dim::X, Dim::Y}, Values{3, 4}).
184 : ///
185 : /// Relation between Dims, Shape, Dimensions and actual data are as follows:
186 : /// 1. If neither Values nor Variances are provided, resulting Variable contains
187 : /// ONLY values of corresponding length.
188 : /// 2. The Variances can't be provided without any Values.
189 : /// 3. Non empty Values and/or Variances must be consistent with shape.
190 : /// 4. If empty Values and/or Variances are provided, resulting Variable
191 : /// contains default initialized Values and/or Variances, the way to make
192 : /// Variable which contains both Values and Variances given length
193 : /// uninitialized is:
194 : /// makeVariable<T>(Dims{Dim::X}, Shape{5}, Values{}, Variances{});
195 682747 : template <class T, class... Ts> Variable makeVariable(Ts &&...ts) {
196 682747 : detail::ArgParser<T> parser;
197 682747 : (parser.parse(std::forward<Ts>(ts)), ...);
198 1365490 : return std::make_from_tuple<Variable>(std::move(parser.args));
199 682747 : }
200 :
201 : template <class... Ts, class... Args>
202 12574 : Variable Variable::construct(const DType &type, Args &&...args) {
203 12574 : std::array vars{core::dtype<Ts> == type
204 : ? makeVariable<Ts>(std::forward<Args>(args)...)
205 : : Variable()...};
206 56031 : for (auto &var : vars)
207 56031 : if (var.is_valid())
208 25148 : return var;
209 0 : throw except::TypeError("Unsupported dtype for constructing a Variable: " +
210 : to_string(type));
211 12574 : }
212 :
213 : template <class... Ts>
214 12574 : Variable::Variable(const DType &type, Ts &&...args)
215 : : Variable{construct<double, float, int64_t, int32_t, bool, std::string,
216 : scipp::core::time_point, scipp::index_pair>(
217 12574 : type, std::forward<Ts>(args)...)} {}
218 :
219 : [[nodiscard]] SCIPP_VARIABLE_EXPORT Variable copy(const Variable &var);
220 : [[maybe_unused]] SCIPP_VARIABLE_EXPORT Variable ©(const Variable &var,
221 : Variable &out);
222 : [[maybe_unused]] SCIPP_VARIABLE_EXPORT Variable copy(const Variable &var,
223 : Variable &&out);
224 :
225 : [[nodiscard]] SCIPP_VARIABLE_EXPORT bool equals_nan(const Variable &a,
226 : const Variable &b);
227 : } // namespace scipp::variable
228 :
229 : namespace scipp::core {
230 : template <> inline constexpr DType dtype<variable::Variable>{1000};
231 : template <> inline constexpr DType dtype<bucket<variable::Variable>>{1001};
232 : } // namespace scipp::core
233 :
234 : namespace scipp {
235 : using variable::Dims;
236 : using variable::makeVariable;
237 : using variable::Shape;
238 : using variable::Values;
239 : using variable::Variable;
240 : using variable::Variances;
241 : } // namespace scipp
242 :
243 : #include "scipp/variable/arithmetic.h"
244 : #include "scipp/variable/logical.h"
|