MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
OperationHelper.hpp
Go to the documentation of this file.
1#pragma once
2
6
8
9namespace MayaFlux::Yantra {
10
11/**
12 * @struct DataStructureInfo
13 * @brief Dimensions, modality, source type, and optional Composite field schema.
14 */
17 std::vector<Kakshya::DataDimension> dimensions;
18 std::type_index original_type = std::type_index(typeid(void));
19 std::optional<Kakshya::CompositeLayout> composite_layout;
20
21 DataStructureInfo() = default;
23 std::vector<Kakshya::DataDimension> dims,
24 std::type_index type)
25 : modality(mod)
26 , dimensions(std::move(dims))
28 {
29 }
30};
31
32/**
33 * @struct CompositeFieldDoubleData
34 * @brief Owning double projection of one Composite field with its presence mask.
35 *
36 * Values for absent elements are zero-filled. Operations must consult presence
37 * before interpreting or changing those values. Source structure retains the
38 * full record layout for field-level reconstruction.
39 */
41 std::vector<double> values;
42 std::vector<uint8_t> presence;
43 std::vector<Kakshya::DataDimension> dimensions;
46 std::string field_name;
47};
48
49namespace detail {
50
51 template <typename T>
52 struct is_composite_operation_data : std::bool_constant<CompositeData<T>> { };
53
54 template <ComputeData T>
55 struct is_composite_operation_data<Datum<T>> : std::bool_constant<CompositeData<T>> { };
56
57 template <typename T>
59
60}
61
62/**
63 * @class OperationHelper
64 * @brief Structure and extraction helpers for Yantra operation data.
65 *
66 * Numeric operations can use this helper to:
67 *
68 * 1. Convert compatible numeric data to std::vector<double>
69 * 2. Process data in double format (universal algorithms)
70 * 3. Reconstruct results back to target ComputeData types
71 *
72 * Key Features:
73 * - Explicit Composite access with field schema and presence-aware projection
74 * - Structure preservation via metadata
75 * - Configurable complex number handling
76 */
77class MAYAFLUX_API OperationHelper {
78public:
79 /** @brief Borrow an owning Composite array without converting its fields. */
81 const Kakshya::CompositeArray& data) noexcept
82 {
83 return data;
84 }
85
86 /**
87 * @brief Borrow the array owned by a Composite container.
88 * @throws std::invalid_argument If the container is null.
89 */
91 const std::shared_ptr<Kakshya::CompositeContainer>& container)
92 {
93 if (!container) {
94 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::Runtime,
95 std::source_location::current(), "Cannot extract data from null CompositeContainer");
96 }
97 return container->get_data();
98 }
99
100 /** @brief Borrow Composite data carried by a Datum. */
101 template <CompositeData T>
103 {
104 return extract_composite_data(datum.data);
105 }
106
107 /**
108 * @brief Borrow Composite records together with their row axis and field layout.
109 * @note The returned array reference remains valid only while the source does.
110 */
111 template <typename T>
112 requires detail::is_composite_operation_data_v<T>
113 static std::pair<std::reference_wrapper<const Kakshya::CompositeArray>, DataStructureInfo>
114 extract_structured_composite(const T& compute_data)
115 {
116 return { std::cref(extract_composite_data(compute_data)),
117 get_structure_info(compute_data) };
118 }
119
120 /**
121 * @brief Project one exact-type numeric Composite field to compatible NDData.
122 * @return Values and a presence mask, or nullopt for a missing or mismatched field.
123 */
124 template <typename ValueT, typename T>
125 requires(detail::is_composite_operation_data_v<T>
126 && ArithmeticData<ValueT> && Kakshya::DataVariantElement<ValueT>)
127 static std::optional<Kakshya::CompositeProjection> project_composite_field(
128 const T& compute_data, std::string_view field_name)
129 {
130 return extract_composite_data(compute_data).template to_nddata<ValueT>(field_name);
131 }
132
133 /**
134 * @brief Extract one compatible numeric field for double-based operations.
135 * @tparam ValueT Exact field type in the Composite layout.
136 * @return Owning values, presence, projected dimensions, and source schema.
137 */
138 template <typename ValueT, typename T>
139 requires(detail::is_composite_operation_data_v<T>
140 && ArithmeticData<ValueT> && Kakshya::DataVariantElement<ValueT>)
141 static std::optional<CompositeFieldDoubleData> extract_projected_composite_double(
142 const T& compute_data, std::string_view field_name)
143 {
144 auto projection = project_composite_field<ValueT>(compute_data, field_name);
145 if (!projection)
146 return std::nullopt;
147
148 const auto& values = std::get<std::vector<ValueT>>(projection->values);
150 result.values.reserve(values.size());
151 for (const auto value : values)
152 result.values.push_back(static_cast<double>(value));
153 result.presence = std::get<std::vector<uint8_t>>(std::move(projection->presence));
154 result.dimensions = std::move(projection->dimensions);
155 result.modality = projection->modality;
156 result.source_structure = get_structure_info(compute_data);
157 result.field_name = field_name;
158 return result;
159 }
160
161 /**
162 * @brief Copy Composite records and replace one projected numeric field.
163 * @tparam ValueT Exact destination field type.
164 * @return Updated records, or nullopt for incompatible shape, type, or value.
165 * @note Unprojected fields are retained; the mask sets field presence.
166 */
167 template <typename ValueT, typename T>
168 requires(detail::is_composite_operation_data_v<T>
169 && ArithmeticData<ValueT> && Kakshya::DataVariantElement<ValueT>)
170 static std::optional<Kakshya::CompositeArray> reconstruct_projected_composite(
171 const T& compute_data, const CompositeFieldDoubleData& projected)
172 {
173 const auto& source = extract_composite_data(compute_data);
174 if (!projected.source_structure.composite_layout)
175 return std::nullopt;
176
177 const auto& original_layout = *projected.source_structure.composite_layout;
178 const auto& current_layout = source.layout();
179 if (original_layout.stride_bytes() != current_layout.stride_bytes()
180 || original_layout.presence_bytes() != current_layout.presence_bytes()
181 || original_layout.fields().size() != current_layout.fields().size())
182 return std::nullopt;
183
184 for (size_t i = 0; i < current_layout.fields().size(); ++i) {
185 const auto& original = original_layout.fields()[i];
186 const auto& current = current_layout.fields()[i];
187 if (original.name != current.name || original.type != current.type
188 || original.offset_bytes != current.offset_bytes
189 || original.size_bytes != current.size_bytes)
190 return std::nullopt;
191 }
192
193 const auto field_index = source.layout().find_field(projected.field_name);
194 if (!field_index || source.layout().fields()[*field_index].type != typeid(ValueT)
195 || projected.values.size() != source.size()
196 || projected.presence.size() != source.size())
197 return std::nullopt;
198
199 Kakshya::CompositeArray result(source);
200 for (size_t row = 0; row < source.size(); ++row) {
201 if (!projected.presence[row]) {
202 if (!result.clear(row, projected.field_name))
203 return std::nullopt;
204 continue;
205 }
206
207 const double value = projected.values[row];
208 if constexpr (std::integral<ValueT>) {
209 if (!std::isfinite(value) || std::trunc(value) != value
210 || value < static_cast<double>(std::numeric_limits<ValueT>::lowest())
211 || value > static_cast<double>(std::numeric_limits<ValueT>::max()))
212 return std::nullopt;
213 } else if constexpr (std::same_as<ValueT, float>) {
214 if (std::isfinite(value)
215 && (value < -static_cast<double>(std::numeric_limits<float>::max())
216 || value > static_cast<double>(std::numeric_limits<float>::max())))
217 return std::nullopt;
218 }
219
220 if (!result.set(row, projected.field_name, static_cast<ValueT>(value)))
221 return std::nullopt;
222 }
223
224 return std::optional<Kakshya::CompositeArray>(std::move(result));
225 }
226
227 /**
228 * @brief Set global complex conversion strategy
229 * @param strategy How to convert complex numbers to doubles
230 */
231 static inline void set_complex_conversion_strategy(Kakshya::ComplexConversionStrategy strategy) { s_complex_strategy = strategy; }
232
233 /**
234 * @brief Get current complex conversion strategy
235 * @return Current conversion strategy
236 */
237 static inline Kakshya::ComplexConversionStrategy get_complex_conversion_strategy() { return s_complex_strategy; }
238
239 /**
240 * @brief extract numeric data from single-variant types
241 * @tparam T ComputeData type
242 * @param compute_data Input data
243 * @return Span of double data
244 */
245 template <typename T>
246 requires SingleVariant<T>
247 static std::span<double> extract_numeric_data(const T& compute_data)
248 {
249 if constexpr (std::is_same_v<T, Kakshya::DataVariant>) {
250 auto const_span = Kakshya::convert_variant<double>(compute_data);
251 return std::span<double>(const_cast<double*>(const_span.data()), const_span.size());
252 }
253 if constexpr (is_eigen_matrix_v<T>) {
254 Kakshya::DataVariant variant = create_data_variant_from_eigen(compute_data);
255 return Kakshya::convert_variant<double>(variant, s_complex_strategy);
256 }
257
258 Kakshya::DataVariant variant { compute_data };
259 return Kakshya::convert_variant_to_double(variant, s_complex_strategy);
260 }
261
262 /**
263 * @brief extract numeric data from multi-variant types
264 * @tparam T ComputeData type
265 * @param compute_data Input data
266 * @return Vector of spans of double data (one per channel/variant)
267 */
268 template <typename T>
270 static std::vector<std::span<double>> extract_numeric_data(const T& compute_data, bool needs_processig = false)
271 {
272 if constexpr (std::is_same_v<T, std::vector<Kakshya::DataVariant>>) {
273 return Kakshya::convert_variants<double>(compute_data, s_complex_strategy);
274 }
275
276 if constexpr (std::is_same_v<T, std::shared_ptr<Kakshya::SignalSourceContainer>>) {
277 if (needs_processig) {
278 if (compute_data->get_processing_state() != Kakshya::ProcessingState::PROCESSED) {
279 compute_data->process_default();
280 compute_data->update_processing_state(Kakshya::ProcessingState::PROCESSED);
281 }
282 std::vector<Kakshya::DataVariant> variant = compute_data->get_processed_data();
283 return Kakshya::convert_variants<double>(variant, s_complex_strategy);
284 }
285 std::vector<Kakshya::DataVariant> variant = compute_data->get_data();
286 return Kakshya::convert_variants<double>(variant, s_complex_strategy);
287 }
288
289 if constexpr (is_eigen_matrix_v<T>)
290 return extract_from_eigen_matrix(compute_data);
291
292 return std::vector<std::span<double>> {};
293 }
294
295 /**
296 * @brief extract numeric data from region-like types
297 * @tparam T ComputeData type
298 * @param compute_data Input data
299 * @param container Container to extract data from
300 * @return Vector of spans of double data (one per region/segment)
301 */
302 template <typename T>
303 requires RegionLike<T>
304 static std::vector<std::span<double>> extract_numeric_data(
305 const T& compute_data,
306 const std::shared_ptr<Kakshya::SignalSourceContainer>& container)
307 {
308 if (!container) {
309 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::ContainerProcessing, std::source_location::current(), "Null container provided for region extraction");
310 }
311
312 if constexpr (std::is_same_v<T, Kakshya::Region>) {
313 auto data = container->get_region_data(compute_data);
314 return Kakshya::convert_variants<double>(data);
315
316 } else if constexpr (std::is_same_v<T, Kakshya::RegionGroup>) {
317 if (compute_data.regions.empty()) {
318 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ContainerProcessing, std::source_location::current(), "Empty RegionGroup cannot be extracted");
319 }
320 auto data = container->get_region_group_data(compute_data);
321 return Kakshya::convert_variants<double>(data);
322
323 } else if constexpr (std::is_same_v<T, std::vector<Kakshya::RegionSegment>>) {
324 if (compute_data.empty()) {
325 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::ContainerProcessing, std::source_location::current(), "Empty RegionSegment vector cannot be extracted");
326 }
327 auto data = container->get_segments_data(compute_data);
328 return Kakshya::convert_variants<double>(data);
329 }
330 }
331
332 // =========================================================================
333 // Native extraction -- no double coercion
334 //
335 // extract_native_data returns data in the source's own scalar type.
336 // Use this for GPU staging, Kinesis::Vision callers, and any path that
337 // should not widen to double.
338 //
339 // For DataVariant / vector<DataVariant> the native type is runtime-
340 // determined; these overloads return DataSpanVariant / vector<DataSpanVariant>
341 // so the caller can branch on element_type() exactly as with get_frame().
342 //
343 // For SignalSourceContainer the native type is queried via value_element_type().
344 // For Eigen the native type is T::Scalar.
345 // Region types are not supported: region data comes out of containers as
346 // DataVariant and is already accessible via the DataVariant overload below.
347 // =========================================================================
348
349 /**
350 * @brief Extract a single DataVariant as a type-erased span without conversion.
351 *
352 * Returns a DataSpanVariant whose active alternative matches the variant's
353 * native element type. No allocation, no coercion.
354 * Call element_type() on the result or use as<E>() when E is known statically.
355 *
356 * @param variant Source variant.
357 * @return DataSpanVariant aliasing the variant's internal storage.
358 */
360 {
361 return std::visit([](const auto& vec) -> Kakshya::DataSpanVariant {
362 return std::span<const typename std::decay_t<decltype(vec)>::value_type>(
363 vec.data(), vec.size());
364 },
365 variant);
366 }
367
368 /**
369 * @brief Extract a vector of DataVariants as type-erased spans without conversion.
370 *
371 * One DataSpanVariant per channel/variant in the input. Each span aliases
372 * the variant's internal storage; no allocation or coercion.
373 *
374 * @param variants Source variants.
375 * @return Per-channel DataSpanVariants.
376 */
377 static std::vector<Kakshya::DataSpanVariant> extract_native_data(
378 const std::vector<Kakshya::DataVariant>& variants)
379 {
380 std::vector<Kakshya::DataSpanVariant> result;
381 result.reserve(variants.size());
382 for (const auto& v : variants)
383 result.push_back(extract_native_data(v));
384 return result;
385 }
386
387 /**
388 * @brief Extract native-typed channel spans from a SignalSourceContainer.
389 *
390 * Queries value_element_type() to determine the native scalar, then
391 * retrieves each channel's data as a FrameView (which already carries the
392 * native span). Returns one DataSpanVariant per channel; no double coercion.
393 *
394 * @param container Source container. Must have data.
395 * @param use_processed If true, reads processed_data; otherwise raw data.
396 * @return Per-channel DataSpanVariants in native element type.
397 */
398 static std::vector<Kakshya::DataSpanVariant> extract_native_data(
399 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
400 bool use_processed = false)
401 {
402 if (!container || !container->has_data())
403 return {};
404
405 const auto& variants = use_processed
406 ? container->get_processed_data()
407 : container->get_data();
408
409 return extract_native_data(variants);
410 }
411
412 /**
413 * @brief Extract native-typed column spans from any Eigen matrix.
414 *
415 * Returns one span per column in T::Scalar, aliasing the matrix's own
416 * storage via Eigen::Map. The matrix must outlive the returned spans.
417 *
418 * @tparam EigenMatrix Any Eigen matrix type. T::Scalar is the native type.
419 * @param matrix Source matrix.
420 * @return Per-column spans in T::Scalar.
421 */
422 template <typename EigenMatrix>
423 requires is_eigen_matrix_v<EigenMatrix>
424 static auto extract_native_data(const EigenMatrix& matrix)
425 -> std::vector<std::span<const typename EigenMatrix::Scalar>>
426 {
427 using Scalar = typename EigenMatrix::Scalar;
428 std::vector<std::span<const Scalar>> result;
429 result.reserve(static_cast<size_t>(matrix.cols()));
430
431 for (int col = 0; col < matrix.cols(); ++col) {
432 result.emplace_back(matrix.col(col).data(), static_cast<size_t>(matrix.rows()));
433 }
434 return result;
435 }
436
437 /**
438 * @brief Convert ComputeData to DataVariant format
439 * @tparam T ComputeData type
440 * @param compute_data Input data
441 * @return Vector of DataVariant (one per channel/variant)
442 */
443 template <typename T>
444 requires MultiVariant<T>
445 static std::vector<Kakshya::DataVariant> to_data_variant(const T& compute_data)
446 {
447 if constexpr (std::is_same_v<T, std::vector<Kakshya::DataVariant>>) {
448 return compute_data;
449 }
450
451 if constexpr (std::is_same_v<T, std::shared_ptr<Kakshya::SignalSourceContainer>>) {
452 if (compute_data->get_processing_state() == Kakshya::ProcessingState::PROCESSED) {
453 return compute_data->get_processed_data();
454 }
455 return compute_data->get_data();
456 }
457
458 if constexpr (is_eigen_matrix_v<T>) {
459 return convert_eigen_matrix_to_variant(compute_data);
460 }
461 }
462
463 /**
464 * @brief Convert region-like ComputeData to DataVariant format
465 * @tparam T ComputeData type
466 * @param compute_data Input data
467 * @param container Container to extract data from
468 * @return Vector of DataVariant (one per region/segment)
469 */
470 template <typename T>
471 requires RegionLike<T>
472 static std::vector<Kakshya::DataVariant> to_data_variant(
473 const T& compute_data,
474 const std::shared_ptr<Kakshya::SignalSourceContainer>& container)
475 {
476 if constexpr (std::is_same_v<T, Kakshya::Region>) {
477 return container->get_region_data(compute_data);
478 } else if constexpr (std::is_same_v<T, Kakshya::RegionGroup>) {
479 return container->get_region_group_data(compute_data);
480 } else if constexpr (std::is_same_v<T, std::vector<Kakshya::RegionSegment>>) {
481 return container->get_segments_data(compute_data);
482 }
483 }
484
485 /**
486 * @brief Populate DataStructureInfo from a Datum without extracting spans.
487 * @tparam T OperationReadyData type.
488 * @param compute_data Source Datum.
489 * @return DataStructureInfo with original_type, dimensions, modality populated.
490 */
491 template <OperationReadyData T>
492 static DataStructureInfo get_structure_info(const T& compute_data)
493 {
494 if constexpr (is_IO<T>::value) {
495 DataStructureInfo info {};
496 info.original_type = std::type_index(typeid(std::decay_t<decltype(compute_data.data)>));
497 info.dimensions = compute_data.dimensions;
498 info.modality = compute_data.modality;
499 if constexpr (CompositeData<std::decay_t<decltype(compute_data.data)>>)
500 info.composite_layout = extract_composite_data(compute_data.data).layout();
501 return info;
502 } else {
503 DataStructureInfo info {};
504 info.original_type = std::type_index(typeid(T));
505 auto [dims, mod] = infer_structure(compute_data);
506 info.dimensions = std::move(dims);
507 info.modality = mod;
508 if constexpr (CompositeData<T>)
509 info.composite_layout = extract_composite_data(compute_data).layout();
510 return info;
511 }
512 }
513
514 /**
515 * @brief Extract structured double data from Datum container or direct ComputeData with automatic container handling
516 * @tparam T OperationReadyData type
517 * @param compute_data or Datum container with data and optional container
518 * @return Tuple of [spans, structure_info]
519 * @throws std::runtime_error if container required but not provided
520 */
521 template <OperationReadyData T>
522 requires(!detail::is_composite_operation_data_v<T>)
523 static std::tuple<std::vector<std::span<double>>, DataStructureInfo>
525 {
526 if constexpr (is_IO<T>::value) {
527 DataStructureInfo info {};
528 info.original_type = std::type_index(typeid(std::decay_t<decltype(compute_data.data)>));
529 info.dimensions = compute_data.dimensions;
530 info.modality = compute_data.modality;
531
532 if constexpr (RequiresContainer<std::decay_t<decltype(compute_data.data)>>) {
533 if (!compute_data.has_container()) {
534 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");
535 }
536 std::vector<std::span<double>> double_data = extract_numeric_data(compute_data.data, compute_data.container.value());
537 return std::make_tuple(double_data, info);
538 } else {
539 std::vector<std::span<double>> double_data = extract_numeric_data(compute_data.data, compute_data.needs_processig());
540 return std::make_tuple(double_data, info);
541 }
542 } else {
543 DataStructureInfo info {};
544 info.original_type = std::type_index(typeid(T));
545 std::vector<std::span<double>> double_data = extract_numeric_data(compute_data);
546 auto [dimensions, modality] = infer_structure(compute_data);
547 info.dimensions = dimensions;
548 info.modality = modality;
549
550 return std::make_tuple(double_data, info);
551 }
552 }
553
554 /**
555 * @brief Extract native-typed channel spans and structure metadata from
556 * a Datum or direct ComputeData, without double coercion.
557 *
558 * Mirrors extract_structured_double in structure: same DataStructureInfo
559 * population, same Datum unwrapping, same container handling for
560 * RegionLike types. The only difference is that channel data is returned
561 * as DataSpanVariant (type-erased native span) rather than span<double>.
562 *
563 * Use this as the entry point for Kinesis::Vision operations and any
564 * other pipeline that must preserve the source's native element type
565 * (uint8_t pixel data, uint16_t depth, float HDR, etc.).
566 *
567 * For RegionLike data types the region variants are returned as
568 * DataSpanVariant via extract_native_data(DataVariant), consistent with
569 * the non-region overloads.
570 *
571 * Callers branch on DataSpanVariant::element_type() or use
572 * FrameView::as<E>() / DataSpanVariant::get_if<span<const E>>() when
573 * the native type is statically known.
574 *
575 * @tparam T OperationReadyData type.
576 * @param compute_data Datum or direct ComputeData to extract from.
577 * @return Tuple of [native channel spans, DataStructureInfo].
578 * @throws std::runtime_error if a container is required but absent.
579 */
580 template <OperationReadyData T>
581 requires(!detail::is_composite_operation_data_v<T>)
582 static std::tuple<std::vector<Kakshya::DataSpanVariant>, DataStructureInfo>
584 {
585 if constexpr (is_IO<T>::value) {
586 DataStructureInfo info {};
587 info.original_type = std::type_index(typeid(std::decay_t<decltype(compute_data.data)>));
588 info.dimensions = compute_data.dimensions;
589 info.modality = compute_data.modality;
590
591 if constexpr (RequiresContainer<std::decay_t<decltype(compute_data.data)>>) {
592 if (!compute_data.has_container()) {
593 error<std::runtime_error>(
594 Journal::Component::Yantra,
595 Journal::Context::ContainerProcessing,
596 std::source_location::current(),
597 "Container is required for region-like data extraction but not provided");
598 }
599
600 const auto region_variants = [&]() -> std::vector<Kakshya::DataVariant> {
601 if constexpr (std::is_same_v<std::decay_t<decltype(compute_data.data)>, Kakshya::Region>) {
602 return compute_data.container.value()->get_region_data(compute_data.data);
603 } else if constexpr (std::is_same_v<std::decay_t<decltype(compute_data.data)>, Kakshya::RegionGroup>) {
604 return compute_data.container.value()->get_region_group_data(compute_data.data);
605 } else {
606 return compute_data.container.value()->get_segments_data(compute_data.data);
607 }
608 }();
609 return { extract_native_data(region_variants), info };
610 } else {
611 auto spans = extract_native_data(compute_data.data);
612 return { std::move(spans), info };
613 }
614 } else {
615 DataStructureInfo info {};
616 info.original_type = std::type_index(typeid(T));
617 auto spans = extract_native_data(compute_data);
618 auto [dimensions, modality] = infer_structure(compute_data);
619 info.dimensions = dimensions;
620 info.modality = modality;
621 return { std::move(spans), info };
622 }
623 }
624
625 /**
626 * @brief Reconstruct ComputeData type from double vector and structure info
627 * @tparam T Target ComputeData type
628 * @param double_data Processed double vector
629 * @param structure_info Original structure metadata
630 * @return Reconstructed data of type T
631 */
632 template <ComputeData T>
633 requires(!is_IO<T>::value && !CompositeData<T>)
634 static T reconstruct_from_double(const std::vector<std::vector<double>>& double_data,
635 const DataStructureInfo& structure_info)
636 {
637 if constexpr (std::is_same_v<T, std::vector<std::vector<double>>>) {
638 return double_data;
639 } else if constexpr (std::is_same_v<T, Eigen::MatrixXd>) {
640 return recreate_eigen_matrix(double_data, structure_info);
641 } else if constexpr (std::is_same_v<T, std::vector<Kakshya::DataVariant>>) {
642 std::vector<Kakshya::DataVariant> variants;
643 variants.reserve(double_data.size());
644 for (const auto& vec : double_data) {
645 variants.emplace_back(vec);
646 }
647 return variants;
648 } else if constexpr (std::is_same_v<T, Kakshya::DataVariant>) {
649 auto data = Kakshya::interleave_channels<double>(double_data);
650 return reconstruct_data_variant_from_double(data, structure_info);
651 } else {
652 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());
653 return T {};
654 }
655 }
656
657 /**
658 * @brief Reconstruct Datum type from double vector and structure info
659 * @tparam T Target Datum type
660 * @param double_data Processed double vector
661 * @param structure_info Original structure metadata
662 * @return Reconstructed data of type T
663 */
664 template <typename T>
665 requires(is_IO<T>::value && !detail::is_composite_operation_data_v<T>)
666 static T reconstruct_from_double(const std::vector<std::vector<double>>& double_data,
667 const DataStructureInfo& structure_info)
668 {
669 using UnderlyingType = std::decay_t<decltype(std::declval<T>().data)>;
670
671 T io_data;
672 io_data.dimensions = structure_info.dimensions;
673 io_data.modality = structure_info.modality;
674
675 io_data.data = reconstruct_from_double<UnderlyingType>(double_data, structure_info);
676
677 return io_data;
678 }
679
680 /**
681 * @brief Setup operation buffer from Datum or ComputeData type
682 * @tparam T Datum or ComputeData type
683 * @param input Datum container or direct ComputeData
684 * @param working_buffer Buffer to setup (will be resized)
685 * @return Tuple of [working_spans, structure_info]
686 */
687 template <OperationReadyData T>
688 requires(!detail::is_composite_operation_data_v<T>)
689 static auto setup_operation_buffer(T& input, std::vector<std::vector<double>>& working_buffer)
690 {
691 auto [data_spans, structure_info] = extract_structured_double(input);
692
693 if (working_buffer.size() != data_spans.size()) {
694 working_buffer.resize(data_spans.size());
695 }
696
697 std::vector<std::span<double>> working_spans(working_buffer.size());
698
699 for (size_t i = 0; i < data_spans.size(); i++) {
700 working_buffer[i].resize(data_spans[i].size());
701 std::ranges::copy(data_spans[i], working_buffer[i].begin());
702 working_spans[i] = std::span<double>(working_buffer[i].data(), working_buffer[i].size());
703 }
704
705 return std::make_tuple(working_spans, structure_info);
706 }
707
708private:
709 static inline Kakshya::ComplexConversionStrategy s_complex_strategy = Kakshya::ComplexConversionStrategy::MAGNITUDE;
710
711 /**
712 * @brief Create DataVariant from Eigen matrix/vector
713 */
714 template <typename EigenType>
715 static Kakshya::DataVariant create_data_variant_from_eigen(const EigenType& eigen_data)
716 {
717 std::vector<double> flat_data;
718
719 if constexpr (EigenType::IsVectorAtCompileTime) {
720 flat_data.resize(eigen_data.size());
721 for (int i = 0; i < eigen_data.size(); ++i) {
722 flat_data[i] = static_cast<double>(eigen_data(i));
723 }
724 } else {
725 flat_data.resize(eigen_data.size());
726 int idx = 0;
727 for (int i = 0; i < eigen_data.rows(); ++i) {
728 for (int j = 0; j < eigen_data.cols(); ++j) {
729 flat_data[idx++] = static_cast<double>(eigen_data(i, j));
730 }
731 }
732 }
733
734 return Kakshya::DataVariant { flat_data };
735 }
736
737 /**
738 * @brief Infer data structure from ComputeData type
739 * @tparam T ComputeData type
740 * @param compute_data Input data
741 * @return Pair of (dimensions, modality)
742 */
743 template <typename EigenMatrix>
744 static std::vector<std::span<double>> extract_from_eigen_matrix(const EigenMatrix& matrix)
745 {
746 static thread_local std::vector<std::vector<double>> columns;
747 columns.clear();
748 columns.resize(matrix.cols());
749 std::vector<std::span<double>> spans;
750 spans.reserve(matrix.cols());
751
752 for (int col = 0; col < matrix.cols(); ++col) {
753 columns[col].resize(matrix.rows());
754 for (int row = 0; row < matrix.rows(); ++row) {
755 columns[col][row] = static_cast<double>(matrix(row, col));
756 }
757 spans.emplace_back(columns[col].data(), columns[col].size());
758 }
759 return spans;
760 }
761
762 /**
763 * @brief Extract data from Eigen vector to double span
764 */
765 /* template <typename EigenVector>
766 static std::span<double> extract_from_eigen_vector(const EigenVector& vec)
767 {
768 thread_local std::vector<double> buffer;
769 buffer.clear();
770 buffer.resize(vec.size());
771
772 for (int i = 0; i < vec.size(); ++i) {
773 buffer[i] = static_cast<double>(vec(i));
774 }
775 return { buffer.data(), buffer.size() };
776 } */
777
778 /**
779 * @brief Convert Eigen matrix to DataVariant format
780 */
781 template <typename EigenMatrix>
782 static std::vector<Kakshya::DataVariant> convert_eigen_matrix_to_variant(const EigenMatrix& matrix)
783 {
784 std::vector<Kakshya::DataVariant> columns(matrix.cols());
785
786 for (int col = 0; col < matrix.cols(); ++col) {
787 auto row_indices = std::views::iota(0, matrix.rows());
788 auto col_data = row_indices
789 | std::views::transform([&](int row) { return static_cast<double>(matrix(row, col)); });
790 columns[col] = { std::vector<double>(col_data.begin(), col_data.end()) };
791 }
792 return columns;
793 }
794
795 /**
796 * @brief Infer data structure from ComputeData type
797 * @tparam T ComputeData type
798 * @param compute_data Input data
799 * @return Pair of (dimensions, modality)
800 */
801 template <typename T>
802 static Eigen::MatrixXd create_eigen_matrix(const std::vector<std::vector<T>>& columns)
803 {
804 if (columns.empty()) {
805 return {};
806 }
807
808 int rows = columns[0].size();
809 int cols = columns.size();
810
811 for (const auto& col : columns) {
812 if (col.size() != rows) {
813 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::Runtime, std::source_location::current(), "All columns must have same size");
814 }
815 }
816
817 Eigen::MatrixXd matrix(rows, cols);
818 for (int col = 0; col < cols; ++col) {
819 for (int row = 0; row < rows; ++row) {
820 matrix(row, col) = static_cast<double>(columns[col][row]);
821 }
822 }
823 return matrix;
824 }
825
826 /**
827 * @brief Create Eigen matrix from spans
828 */
829 template <typename T>
830 static Eigen::MatrixXd create_eigen_matrix(const std::vector<std::span<const T>>& spans)
831 {
832 if (spans.empty()) {
833 return {};
834 }
835
836 int rows = spans[0].size();
837 int cols = spans.size();
838
839 for (const auto& span : spans) {
840 if (span.size() != rows) {
841 error<std::invalid_argument>(Journal::Component::Yantra, Journal::Context::Runtime, std::source_location::current(), "All spans must have same size");
842 }
843 }
844
845 Eigen::MatrixXd matrix(rows, cols);
846 for (int col = 0; col < cols; ++col) {
847 for (int row = 0; row < rows; ++row) {
848 matrix(row, col) = static_cast<double>(spans[col][row]);
849 }
850 }
851 return matrix;
852 }
853
854 /**
855 * @brief Infer data structure from ComputeData type
856 * @tparam T ComputeData type
857 * @param compute_data Input data
858 * @param container Optional container for region-like types
859 * @return Pair of (dimensions, modality)
860 */
861 static Eigen::MatrixXd recreate_eigen_matrix(
862 const std::vector<std::vector<double>>& columns,
863 const DataStructureInfo& structure_info);
864
865 /**
866 * @brief Infer data structure from ComputeData type
867 * @tparam T ComputeData type
868 * @param compute_data Input data
869 * @param container Optional container for region-like types
870 * @return Pair of (dimensions, modality)
871 */
872 static Eigen::MatrixXd recreate_eigen_matrix(
873 const std::vector<std::span<const double>>& spans,
874 const DataStructureInfo& structure_info);
875
876 /**
877 * @brief Reconstruct DataVariant from double data and structure info
878 */
879 static Kakshya::DataVariant reconstruct_data_variant_from_double(const std::vector<double>& double_data,
880 const DataStructureInfo& structure_info);
881};
882
883} // namespace MayaFlux::Yantra
Core::GlobalInputConfig input
Definition Config.cpp:42
bool size
uint32_t rows
uint32_t columns
glm::vec2 current
glm::vec3 value
std::vector< std::byte > values
Definition VDBWriter.cpp:26
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
bool clear(size_t index, std::string_view field_name)
Mark a field absent without removing its stored bytes.
bool set(size_t index, std::string_view field_name, T value)
Write an arithmetic field of an existing element.
Owns a sequence of values that share a CompositeLayout.
static Eigen::MatrixXd create_eigen_matrix(const std::vector< std::span< const T > > &spans)
Create Eigen matrix from spans.
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 const Kakshya::CompositeArray & extract_composite_data(const Datum< T > &datum)
Borrow Composite data carried by a Datum.
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 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::optional< CompositeFieldDoubleData > extract_projected_composite_double(const T &compute_data, std::string_view field_name)
Extract one compatible numeric field for double-based operations.
static std::pair< std::reference_wrapper< const Kakshya::CompositeArray >, DataStructureInfo > extract_structured_composite(const T &compute_data)
Borrow Composite records together with their row axis and field layout.
static std::vector< Kakshya::DataSpanVariant > extract_native_data(const std::shared_ptr< Kakshya::SignalSourceContainer > &container, bool use_processed=false)
Extract native-typed channel spans from a SignalSourceContainer.
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::optional< Kakshya::CompositeArray > reconstruct_projected_composite(const T &compute_data, const CompositeFieldDoubleData &projected)
Copy Composite records and replace one projected numeric field.
static 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 std::tuple< std::vector< Kakshya::DataSpanVariant >, DataStructureInfo > extract_structured_native(T &compute_data)
Extract native-typed channel spans and structure metadata from a Datum or direct ComputeData,...
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 Kakshya::DataSpanVariant extract_native_data(const Kakshya::DataVariant &variant)
Extract a single DataVariant as a type-erased span without conversion.
static std::vector< Kakshya::DataSpanVariant > extract_native_data(const std::vector< Kakshya::DataVariant > &variants)
Extract a vector of DataVariants as type-erased spans without conversion.
static const Kakshya::CompositeArray & extract_composite_data(const Kakshya::CompositeArray &data) noexcept
Borrow an owning Composite array without converting its fields.
static void set_complex_conversion_strategy(Kakshya::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 DataStructureInfo get_structure_info(const T &compute_data)
Populate DataStructureInfo from a Datum without extracting spans.
static std::optional< Kakshya::CompositeProjection > project_composite_field(const T &compute_data, std::string_view field_name)
Project one exact-type numeric Composite field to compatible NDData.
static const Kakshya::CompositeArray & extract_composite_data(const std::shared_ptr< Kakshya::CompositeContainer > &container)
Borrow the array owned by a Composite container.
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 auto extract_native_data(const EigenMatrix &matrix) -> std::vector< std::span< const typename EigenMatrix::Scalar > >
Extract native-typed column spans from any Eigen matrix.
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 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.
Structure and extraction helpers for Yantra operation data.
Concept to constrain types to valid DataVariant elements.
Definition NDData.hpp:127
Composite data that retains its field layout and presence information.
Definition DataSpec.hpp:49
Any Eigen matrix type, regardless of scalar type.
Definition DataSpec.hpp:121
Types that yield multiple data channels on extraction.
Definition DataSpec.hpp:103
Types that represent spatial or temporal markers requiring a container to resolve data.
Definition DataSpec.hpp:96
Types that need an associated SignalSourceContainer to extract data.
Definition DataSpec.hpp:110
Single data source: one DataVariant, a column Eigen vector of any scalar type, or any type constructi...
Definition DataSpec.hpp:130
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
Definition NDData.hpp:625
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:70
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:132
@ UNKNOWN
Unknown or undefined modality.
@ TENSOR_ND
N-dimensional tensor.
ComplexConversionStrategy
Strategy for converting complex numbers to real values.
Definition DataUtils.hpp:15
constexpr bool is_composite_operation_data_v
Organizes related signal regions into a categorized collection.
Represents a point or span in N-dimensional space.
Definition Region.hpp:73
std::vector< Kakshya::DataDimension > dimensions
Owning double projection of one Composite field with its presence mask.
std::optional< Kakshya::CompositeLayout > composite_layout
DataStructureInfo(Kakshya::DataModality mod, std::vector< Kakshya::DataDimension > dims, std::type_index type)
std::vector< Kakshya::DataDimension > dimensions
Dimensions, modality, source type, and optional Composite field schema.
T data
The actual computation data.
Definition DataIO.hpp:27
Input/Output container for computation pipeline data flow with structure preservation.
Definition DataIO.hpp:26
Helper to detect if a type is an Datum.
Definition DataIO.hpp:332