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