MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
OperationHelper.hpp
Go to the documentation of this file.
1#pragma once
2
5
7
8#include <typeindex>
9
10namespace MayaFlux::Yantra {
11
12/**
13 * @struct DataStructureInfo
14 * @brief Metadata about data structure for reconstruction
15 */
18 std::vector<Kakshya::DataDimension> dimensions;
19 std::type_index original_type = std::type_index(typeid(void));
20
21 DataStructureInfo() = default;
23 std::vector<Kakshya::DataDimension> dims,
24 std::type_index type)
25 : modality(mod)
26 , dimensions(std::move(dims))
27 , original_type(type)
28 {
29 }
30};
31
32/**
33 * @class OperationHelper
34 * @brief Universal data conversion helper for all Yantra operations
35 *
36 * Provides a unified interface for converting between ComputeData types and
37 * processing formats. All operations (analyzers, sorters, extractors, transformers)
38 * can use this helper to:
39 *
40 * 1. Convert any ComputeData → DataVariant → std::vector<double>
41 * 2. Process data in double format (universal algorithms)
42 * 3. Reconstruct results back to target ComputeData types
43 *
44 * Key Features:
45 * - Universal conversion path for all ComputeData types
46 * - Structure preservation via metadata
47 * - Configurable complex number handling
48 * - Lossless conversions (except complex → double)
49 * - Thread-safe operation
50 */
52public:
53 /**
54 * @brief Set global complex conversion strategy
55 * @param strategy How to convert complex numbers to doubles
56 */
58
59 /**
60 * @brief Get current complex conversion strategy
61 * @return Current conversion strategy
62 */
64
65 /**
66 * @brief extract numeric data from single-variant types
67 * @tparam T ComputeData type
68 * @param compute_data Input data
69 * @return Span of double data
70 */
71 template <typename T>
72 requires SingleVariant<T>
73 static std::span<double> extract_numeric_data(const T& compute_data)
74 {
75 if constexpr (std::is_same_v<T, Kakshya::DataVariant>) {
76 auto const_span = Kakshya::convert_variant<double>(compute_data);
77 return std::span<double>(const_cast<double*>(const_span.data()), const_span.size());
78 }
79 if constexpr (std::is_base_of_v<Eigen::MatrixBase<T>, T>) {
81 return Kakshya::convert_variant<double>(variant, s_complex_strategy);
82 }
83
84 Kakshya::DataVariant variant { compute_data };
86 }
87
88 /**
89 * @brief extract numeric data from multi-variant types
90 * @tparam T ComputeData type
91 * @param compute_data Input data
92 * @return Vector of spans of double data (one per channel/variant)
93 */
94 template <typename T>
96 static std::vector<std::span<double>> extract_numeric_data(const T& compute_data, bool needs_processig = false)
97 {
98 if constexpr (std::is_same_v<T, std::vector<Kakshya::DataVariant>>) {
99 return Kakshya::convert_variants<double>(compute_data, s_complex_strategy);
100 }
101
102 if constexpr (std::is_same_v<T, std::shared_ptr<Kakshya::SignalSourceContainer>>) {
103 if (needs_processig) {
104 if (compute_data->get_processing_state() != Kakshya::ProcessingState::PROCESSED) {
105 compute_data->process_default();
106 compute_data->update_processing_state(Kakshya::ProcessingState::PROCESSED);
107 }
108 std::vector<Kakshya::DataVariant> variant = compute_data->get_processed_data();
109 return Kakshya::convert_variants<double>(variant, s_complex_strategy);
110 }
111 std::vector<Kakshya::DataVariant> variant = compute_data->get_data();
112 return Kakshya::convert_variants<double>(variant, s_complex_strategy);
113 }
114
115 if constexpr (std::is_base_of_v<Eigen::MatrixBase<T>, T>)
116 return extract_from_eigen_matrix(compute_data);
117
118 return std::vector<std::span<double>> {};
119 }
120
121 /**
122 * @brief extract numeric data from region-like types
123 * @tparam T ComputeData type
124 * @param compute_data Input data
125 * @param container Container to extract data from
126 * @return Vector of spans of double data (one per region/segment)
127 */
128 template <typename T>
129 requires RegionLike<T>
130 static std::vector<std::span<double>> extract_numeric_data(
131 const T& compute_data,
132 const std::shared_ptr<Kakshya::SignalSourceContainer>& container)
133 {
134 if (!container) {
135 throw std::invalid_argument("Null container provided for region extraction");
136 }
137
138 if constexpr (std::is_same_v<T, Kakshya::Region>) {
139 auto data = container->get_region_data(compute_data);
140 return Kakshya::convert_variants<double>(data);
141
142 } else if constexpr (std::is_same_v<T, Kakshya::RegionGroup>) {
143 if (compute_data.regions.empty()) {
144 throw std::runtime_error("Empty RegionGroup cannot be extracted");
145 }
146 auto data = container->get_region_group_data(compute_data);
147 return Kakshya::convert_variants<double>(data);
148
149 } else if constexpr (std::is_same_v<T, std::vector<Kakshya::RegionSegment>>) {
150 if (compute_data.empty()) {
151 throw std::runtime_error("RegionSegment contains no extractable data");
152 }
153 auto data = container->get_segments_data(compute_data);
154 return Kakshya::convert_variants<double>(data);
155 }
156 }
157
158 /**
159 * @brief Convert ComputeData to DataVariant format
160 * @tparam T ComputeData type
161 * @param compute_data Input data
162 * @return Vector of DataVariant (one per channel/variant)
163 */
164 template <typename T>
165 requires MultiVariant<T>
166 static std::vector<Kakshya::DataVariant> to_data_variant(const T& compute_data)
167 {
168 if constexpr (std::is_same_v<T, std::vector<Kakshya::DataVariant>>) {
169 return compute_data;
170 }
171
172 if constexpr (std::is_same_v<T, std::shared_ptr<Kakshya::SignalSourceContainer>>) {
173 if (compute_data->get_processing_state() == Kakshya::ProcessingState::PROCESSED) {
174 return compute_data->get_processed_data();
175 }
176 return compute_data->get_data();
177 }
178
179 if constexpr (std::is_base_of_v<Eigen::MatrixBase<T>, T>) {
180 return convert_eigen_matrix_to_variant(compute_data);
181 }
182 }
183
184 /**
185 * @brief Convert region-like ComputeData to DataVariant format
186 * @tparam T ComputeData type
187 * @param compute_data Input data
188 * @param container Container to extract data from
189 * @return Vector of DataVariant (one per region/segment)
190 */
191 template <typename T>
192 requires RegionLike<T>
193 static std::vector<Kakshya::DataVariant> to_data_variant(
194 const T& compute_data,
195 const std::shared_ptr<Kakshya::SignalSourceContainer>& container)
196 {
197 if constexpr (std::is_same_v<T, Kakshya::Region>) {
198 return container->get_region_data(compute_data);
199 } else if constexpr (std::is_same_v<T, Kakshya::RegionGroup>) {
200 return container->get_region_group_data(compute_data);
201 } else if constexpr (std::is_same_v<T, std::vector<Kakshya::RegionSegment>>) {
202 return container->get_segments_data(compute_data);
203 }
204 }
205
206 /**
207 * @brief Extract structured double data from Datum container or direct ComputeData with automatic container handling
208 * @tparam T OperationReadyData type
209 * @param compute_data or Datum container with data and optional container
210 * @return Tuple of [spans, structure_info]
211 * @throws std::runtime_error if container required but not provided
212 */
213 template <OperationReadyData T>
214 static std::tuple<std::vector<std::span<double>>, DataStructureInfo>
216 {
217 if constexpr (is_IO<T>::value) {
218 DataStructureInfo info {};
219 info.original_type = std::type_index(typeid(std::decay_t<decltype(compute_data.data)>));
220 info.dimensions = compute_data.dimensions;
221 info.modality = compute_data.modality;
222
223 if constexpr (RequiresContainer<std::decay_t<decltype(compute_data.data)>>) {
224 if (!compute_data.has_container()) {
225 throw std::runtime_error("Container is required for region-like data extraction but not provided");
226 }
227 std::vector<std::span<double>> double_data = extract_numeric_data(compute_data.data, compute_data.container.value());
228 return std::make_tuple(double_data, info);
229 } else {
230 std::vector<std::span<double>> double_data = extract_numeric_data(compute_data.data, compute_data.needs_processig());
231 return std::make_tuple(double_data, info);
232 }
233 } else {
234 DataStructureInfo info {};
235 info.original_type = std::type_index(typeid(T));
236 std::vector<std::span<double>> double_data = extract_numeric_data(compute_data);
237 auto [dimensions, modality] = infer_structure(compute_data);
238 info.dimensions = dimensions;
239 info.modality = modality;
240
241 return std::make_tuple(double_data, info);
242 }
243 }
244
245 /**
246 * @brief Reconstruct ComputeData type from double vector and structure info
247 * @tparam T Target ComputeData type
248 * @param double_data Processed double vector
249 * @param structure_info Original structure metadata
250 * @return Reconstructed data of type T
251 */
252 template <ComputeData T>
253 requires(!is_IO<T>::value)
254 static T reconstruct_from_double(const std::vector<std::vector<double>>& double_data,
255 const DataStructureInfo& structure_info)
256 {
257 if constexpr (std::is_same_v<T, std::vector<std::vector<double>>>) {
258 return double_data;
259 } else if constexpr (std::is_same_v<T, Eigen::MatrixXd>) {
260 return recreate_eigen_matrix(double_data, structure_info);
261 } else if constexpr (std::is_same_v<T, std::vector<Kakshya::DataVariant>>) {
262 std::vector<Kakshya::DataVariant> variants;
263 variants.reserve(double_data.size());
264 for (const auto& vec : double_data) {
265 variants.emplace_back(vec);
266 }
267 return variants;
268 } else if constexpr (std::is_same_v<T, Kakshya::DataVariant>) {
269 auto data = Kakshya::interleave_channels<double>(double_data);
270 return reconstruct_data_variant_from_double(data, structure_info);
271 } else {
272 throw std::runtime_error("Reconstruction not implemented for target type");
273 }
274 }
275
276 /**
277 * @brief Reconstruct Datum type from double vector and structure info
278 * @tparam T Target Datum type
279 * @param double_data Processed double vector
280 * @param structure_info Original structure metadata
281 * @return Reconstructed data of type T
282 */
283 template <typename T>
284 requires is_IO<T>::value
285 static T reconstruct_from_double(const std::vector<std::vector<double>>& double_data,
286 const DataStructureInfo& structure_info)
287 {
288 using UnderlyingType = std::decay_t<decltype(std::declval<T>().data)>;
289
290 T io_data;
291 io_data.dimensions = structure_info.dimensions;
292 io_data.modality = structure_info.modality;
293
294 io_data.data = reconstruct_from_double<UnderlyingType>(double_data, structure_info);
295
296 return io_data;
297 }
298
299 /**
300 * @brief Setup operation buffer from Datum or ComputeData type
301 * @tparam T Datum or ComputeData type
302 * @param input Datum container or direct ComputeData
303 * @param working_buffer Buffer to setup (will be resized)
304 * @return Tuple of [working_spans, structure_info]
305 */
306 template <OperationReadyData T>
307 static auto setup_operation_buffer(T& input, std::vector<std::vector<double>>& working_buffer)
308 {
309 auto [data_spans, structure_info] = extract_structured_double(input);
310
311 if (working_buffer.size() != data_spans.size()) {
312 working_buffer.resize(data_spans.size());
313 }
314
315 std::vector<std::span<double>> working_spans(working_buffer.size());
316
317 for (size_t i = 0; i < data_spans.size(); i++) {
318 working_buffer[i].resize(data_spans[i].size());
319 std::ranges::copy(data_spans[i], working_buffer[i].begin());
320 working_spans[i] = std::span<double>(working_buffer[i].data(), working_buffer[i].size());
321 }
322
323 return std::make_tuple(working_spans, structure_info);
324 }
325
326private:
328
329 /**
330 * @brief Create DataVariant from Eigen matrix/vector
331 */
332 template <typename EigenType>
333 static Kakshya::DataVariant create_data_variant_from_eigen(const EigenType& eigen_data)
334 {
335 std::vector<double> flat_data;
336
337 if constexpr (EigenType::IsVectorAtCompileTime) {
338 flat_data.resize(eigen_data.size());
339 for (int i = 0; i < eigen_data.size(); ++i) {
340 flat_data[i] = static_cast<double>(eigen_data(i));
341 }
342 } else {
343 flat_data.resize(eigen_data.size());
344 int idx = 0;
345 for (int i = 0; i < eigen_data.rows(); ++i) {
346 for (int j = 0; j < eigen_data.cols(); ++j) {
347 flat_data[idx++] = static_cast<double>(eigen_data(i, j));
348 }
349 }
350 }
351
352 return Kakshya::DataVariant { flat_data };
353 }
354
355 /**
356 * @brief Infer data structure from ComputeData type
357 * @tparam T ComputeData type
358 * @param compute_data Input data
359 * @return Pair of (dimensions, modality)
360 */
361 template <typename EigenMatrix>
362 static std::vector<std::span<double>> extract_from_eigen_matrix(const EigenMatrix& matrix)
363 {
364 static thread_local std::vector<std::vector<double>> columns;
365 columns.clear();
366 columns.resize(matrix.cols());
367 std::vector<std::span<double>> spans;
368 spans.reserve(matrix.cols());
369
370 for (int col = 0; col < matrix.cols(); ++col) {
371 columns[col].resize(matrix.rows());
372 for (int row = 0; row < matrix.rows(); ++row) {
373 columns[col][row] = static_cast<double>(matrix(row, col));
374 }
375 spans.emplace_back(columns[col].data(), columns[col].size());
376 }
377 return spans;
378 }
379
380 /**
381 * @brief Extract data from Eigen vector to double span
382 */
383 /* template <typename EigenVector>
384 static std::span<double> extract_from_eigen_vector(const EigenVector& vec)
385 {
386 thread_local std::vector<double> buffer;
387 buffer.clear();
388 buffer.resize(vec.size());
389
390 for (int i = 0; i < vec.size(); ++i) {
391 buffer[i] = static_cast<double>(vec(i));
392 }
393 return { buffer.data(), buffer.size() };
394 } */
395
396 /**
397 * @brief Convert Eigen matrix to DataVariant format
398 */
399 template <typename EigenMatrix>
400 static std::vector<Kakshya::DataVariant> convert_eigen_matrix_to_variant(const EigenMatrix& matrix)
401 {
402 std::vector<Kakshya::DataVariant> columns(matrix.cols());
403
404 for (int col = 0; col < matrix.cols(); ++col) {
405 auto row_indices = std::views::iota(0, matrix.rows());
406 auto col_data = row_indices
407 | std::views::transform([&](int row) { return static_cast<double>(matrix(row, col)); });
408 columns[col] = { std::vector<double>(col_data.begin(), col_data.end()) };
409 }
410 return columns;
411 }
412
413 /**
414 * @brief Infer data structure from ComputeData type
415 * @tparam T ComputeData type
416 * @param compute_data Input data
417 * @return Pair of (dimensions, modality)
418 */
419 template <typename T>
420 static Eigen::MatrixXd create_eigen_matrix(const std::vector<std::vector<T>>& columns)
421 {
422 if (columns.empty()) {
423 return {};
424 }
425
426 int rows = columns[0].size();
427 int cols = columns.size();
428
429 for (const auto& col : columns) {
430 if (col.size() != rows) {
431 throw std::invalid_argument("All columns must have same size");
432 }
433 }
434
435 Eigen::MatrixXd matrix(rows, cols);
436 for (int col = 0; col < cols; ++col) {
437 for (int row = 0; row < rows; ++row) {
438 matrix(row, col) = static_cast<double>(columns[col][row]);
439 }
440 }
441 return matrix;
442 }
443
444 /**
445 * @brief Create Eigen matrix from spans
446 */
447 template <typename T>
448 static Eigen::MatrixXd create_eigen_matrix(const std::vector<std::span<const T>>& spans)
449 {
450 if (spans.empty()) {
451 return {};
452 }
453
454 int rows = spans[0].size();
455 int cols = spans.size();
456
457 for (const auto& span : spans) {
458 if (span.size() != rows) {
459 throw std::invalid_argument("All spans must have same size");
460 }
461 }
462
463 Eigen::MatrixXd matrix(rows, cols);
464 for (int col = 0; col < cols; ++col) {
465 for (int row = 0; row < rows; ++row) {
466 matrix(row, col) = static_cast<double>(spans[col][row]);
467 }
468 }
469 return matrix;
470 }
471
472 /**
473 * @brief Infer data structure from ComputeData type
474 * @tparam T ComputeData type
475 * @param compute_data Input data
476 * @param container Optional container for region-like types
477 * @return Pair of (dimensions, modality)
478 */
479 static Eigen::MatrixXd recreate_eigen_matrix(
480 const std::vector<std::vector<double>>& columns,
481 const DataStructureInfo& structure_info);
482
483 /**
484 * @brief Infer data structure from ComputeData type
485 * @tparam T ComputeData type
486 * @param compute_data Input data
487 * @param container Optional container for region-like types
488 * @return Pair of (dimensions, modality)
489 */
490 static Eigen::MatrixXd recreate_eigen_matrix(
491 const std::vector<std::span<const double>>& spans,
492 const DataStructureInfo& structure_info);
493
494 /**
495 * @brief Reconstruct DataVariant from double data and structure info
496 */
497 static Kakshya::DataVariant reconstruct_data_variant_from_double(const std::vector<double>& double_data,
498 const DataStructureInfo& structure_info);
499};
500
501} // namespace MayaFlux::Yantra
static Eigen::MatrixXd create_eigen_matrix(const std::vector< std::span< const T > > &spans)
Create Eigen matrix from spans.
static Kakshya::DataVariant reconstruct_data_variant_from_double(const std::vector< double > &double_data, const DataStructureInfo &structure_info)
Reconstruct DataVariant from double data and structure info.
static std::vector< std::span< double > > extract_from_eigen_matrix(const EigenMatrix &matrix)
Infer data structure from ComputeData type.
static Kakshya::ComplexConversionStrategy get_complex_conversion_strategy()
Get current complex conversion strategy.
static ::value T reconstruct_from_double(const std::vector< std::vector< double > > &double_data, const DataStructureInfo &structure_info)
Reconstruct Datum type from double vector and structure info.
static Eigen::MatrixXd create_eigen_matrix(const std::vector< std::vector< T > > &columns)
Infer data structure from ComputeData type.
static std::vector< Kakshya::DataVariant > convert_eigen_matrix_to_variant(const EigenMatrix &matrix)
Extract data from Eigen vector to double span.
static std::vector< Kakshya::DataVariant > to_data_variant(const T &compute_data, const std::shared_ptr< Kakshya::SignalSourceContainer > &container)
Convert region-like ComputeData to DataVariant format.
static Kakshya::ComplexConversionStrategy s_complex_strategy
static std::vector< std::span< double > > extract_numeric_data(const T &compute_data, const std::shared_ptr< Kakshya::SignalSourceContainer > &container)
extract numeric data from region-like types
static void set_complex_conversion_strategy(Kakshya::ComplexConversionStrategy strategy)
Set global complex conversion strategy.
static std::tuple< std::vector< std::span< double > >, DataStructureInfo > extract_structured_double(T &compute_data)
Extract structured double data from Datum container or direct ComputeData with automatic container ha...
static std::vector< std::span< double > > extract_numeric_data(const T &compute_data, bool needs_processig=false)
extract numeric data from multi-variant types
static auto setup_operation_buffer(T &input, std::vector< std::vector< double > > &working_buffer)
Setup operation buffer from Datum or ComputeData type.
static Eigen::MatrixXd recreate_eigen_matrix(const std::vector< std::vector< double > > &columns, const DataStructureInfo &structure_info)
Infer data structure from ComputeData type.
static std::vector< Kakshya::DataVariant > to_data_variant(const T &compute_data)
Convert ComputeData to DataVariant format.
static std::span< double > extract_numeric_data(const T &compute_data)
extract numeric data from single-variant types
static Kakshya::DataVariant create_data_variant_from_eigen(const EigenType &eigen_data)
Create DataVariant from Eigen matrix/vector.
static T reconstruct_from_double(const std::vector< std::vector< double > > &double_data, const DataStructureInfo &structure_info)
Reconstruct ComputeData type from double vector and structure info.
Universal data conversion helper for all Yantra operations.
Concept for Eigen matrix types with double scalar.
Definition DataSpec.hpp:221
A SingleVariant is either a single DataVariant, an Eigen vector type, or any type constructible from ...
Definition DataSpec.hpp:98
@ PROCESSED
Container has completed processing and results are available.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:73
std::span< double > convert_variant_to_double(DataVariant &data, ComplexConversionStrategy strategy=ComplexConversionStrategy::MAGNITUDE)
Convert variant to double span.
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:78
@ UNKNOWN
Unknown or undefined modality.
ComplexConversionStrategy
Strategy for converting complex numbers to real values.
Definition DataUtils.hpp:16
@ MAGNITUDE
|z| = sqrt(real² + imag²)
static std::pair< std::vector< Kakshya::DataDimension >, Kakshya::DataModality > infer_structure(const T &data, const std::shared_ptr< Kakshya::SignalSourceContainer > &container=nullptr)
Infer dimensions and modality from any ComputeData type.
DataStructureInfo(Kakshya::DataModality mod, std::vector< Kakshya::DataDimension > dims, std::type_index type)
std::vector< Kakshya::DataDimension > dimensions
Metadata about data structure for reconstruction.
Helper to detect if a type is an Datum.
Definition DataIO.hpp:332