MayaFlux 0.4.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 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ContainerProcessing, std::source_location::current(), "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 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ContainerProcessing, std::source_location::current(), "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 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ContainerProcessing, std::source_location::current(), "Empty RegionSegment vector cannot be extracted");
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 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ContainerProcessing, std::source_location::current(), "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 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::Runtime, std::source_location::current(), "Reconstruction not implemented for target type {}", structure_info.original_type.name());
273 return T {};
274 }
275 }
276
277 /**
278 * @brief Reconstruct Datum type from double vector and structure info
279 * @tparam T Target Datum 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 Datum or ComputeData type
302 * @tparam T Datum or ComputeData type
303 * @param input Datum 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 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::Runtime, std::source_location::current(), "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 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::Runtime, std::source_location::current(), "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
Core::GlobalInputConfig input
Definition Config.cpp:36
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:220
A SingleVariant is either a single DataVariant, an Eigen vector type, or any type constructible from ...
Definition DataSpec.hpp:97
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Runtime
General runtime operations (default fallback)
@ Yantra
DSP algorithms, computational units, matrix operations, Grammar.
@ 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:76
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:81
@ 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:329