MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ComputePipeline.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "ComputeGrammar.hpp"
4#include "ComputeMatrix.hpp"
5
6namespace MayaFlux::Yantra {
7
8/**
9 * @class ComputationPipeline
10 * @brief Pipeline that uses grammar rules for operation composition
11 * @tparam InputType Input data type (defaults to std::vector<Kakshya::DataVariant>)
12 * @tparam OutputType Output data type (defaults to InputType)
13 *
14 * The ComputationPipeline provides a flexible, grammar-aware system for chaining
15 * computational operations in sequence. Unlike traditional pipelines that execute
16 * operations in a fixed order, this pipeline can dynamically select and apply
17 * operations based on grammar rules that match input data characteristics and
18 * execution context.
19 *
20 * ## Key Features
21 *
22 * **Grammar Integration**: Uses ComputationGrammar to intelligently select and
23 * configure operations based on input data properties and context.
24 *
25 * **Type Safety**: Template-based design ensures type compatibility between
26 * pipeline stages while supporting different input/output types.
27 *
28 * **Dynamic Configuration**: Operations can be added, configured, and removed
29 * at runtime, enabling adaptive processing workflows.
30 *
31 * **Error Handling**: Comprehensive error handling with operation-specific
32 * error reporting and graceful degradation.
33 *
34 * ## Usage Patterns
35 *
36 * ### Basic Pipeline Construction
37 * ```cpp
38 * auto pipeline = std::make_shared<ComputationPipeline<std::vector<Kakshya::DataVariant>>>();
39 *
40 * // Add operations with names for later reference
41 * pipeline->create_operation<MathematicalTransformer<>>("gain_stage")
42 * ->create_operation<SpectralTransformer<>>("frequency_processing")
43 * ->create_operation<TemporalTransformer<>>("time_effects");
44 * ```
45 *
46 * ### Grammar-Driven Processing
47 * ```cpp
48 * // Pipeline automatically applies grammar rules before operation chain
49 * ExecutionContext context;
50 * context.execution_metadata["processing_mode"] = std::string("high_quality");
51 *
52 * auto result = pipeline->process(input_data, context);
53 * // Grammar rules are evaluated first, then operation chain executes
54 * ```
55 *
56 * ### Dynamic Operation Configuration
57 * ```cpp
58 * // Configure specific operations by name
59 * pipeline->configure_operation<MathematicalTransformer<>>("gain_stage",
60 * [](auto op) {
61 * op->set_parameter("operation", "gain");
62 * op->set_parameter("gain_factor", 2.0);
63 * });
64 * ```
65 */
66template <ComputeData InputType = std::vector<Kakshya::DataVariant>, ComputeData OutputType = InputType>
67class MAYAFLUX_API ComputationPipeline {
68public:
71
72 /**
73 * @brief Constructor with optional grammar
74 * @param grammar Shared pointer to ComputationGrammar instance (creates new if nullptr)
75 *
76 * Creates a pipeline with the specified grammar instance. If no grammar is provided,
77 * creates a new empty grammar that can be populated with rules later.
78 */
79 explicit ComputationPipeline(std::shared_ptr<ComputationGrammar> grammar = nullptr)
80 : m_grammar(grammar ? std::move(grammar) : std::make_shared<ComputationGrammar>())
81 {
82 }
83
84 /**
85 * @brief Add a concrete operation instance to the pipeline
86 * @tparam ConcreteOpType The concrete operation type (must derive from ComputeOperation)
87 * @param operation Shared pointer to the operation instance
88 * @param name Optional name for the operation (used for later reference)
89 * @return Reference to this pipeline for method chaining
90 *
91 * Adds an existing operation instance to the pipeline. The operation will be executed
92 * in the order it was added. Names are optional but recommended for later configuration
93 * and debugging.
94 *
95 * @note The operation type must be compatible with the pipeline's input/output types
96 */
97 template <typename ConcreteOpType>
98 ComputationPipeline& add_operation(std::shared_ptr<ConcreteOpType> operation, const std::string& name = "")
99 {
100 static_assert(std::is_base_of_v<ComputeOperation<InputType, OutputType>, ConcreteOpType>,
101 "Operation must derive from ComputeOperation");
102
103 m_operations.emplace_back(std::static_pointer_cast<ComputeOperation<InputType, OutputType>>(operation), name);
104 return *this;
105 }
106
107 /**
108 * @brief Create and add operation by type
109 * @tparam ConcreteOpType The concrete operation type to create
110 * @tparam Args Constructor argument types
111 * @param name Optional name for the operation
112 * @param args Constructor arguments for the operation
113 * @return Reference to this pipeline for method chaining
114 *
115 * Creates a new instance of the specified operation type and adds it to the pipeline.
116 * This is the most convenient way to add operations when you don't need to configure
117 * them before adding to the pipeline.
118 *
119 * Example:
120 * ```cpp
121 * pipeline->create_operation<MathematicalTransformer<>>("gain")
122 * ->create_operation<SpectralTransformer<>>("pitch_shift",
123 * SpectralOperation::PITCH_SHIFT);
124 * ```
125 */
126 template <typename ConcreteOpType, typename... Args>
127 ComputationPipeline& create_operation(const std::string& name = "", Args&&... args)
128 {
129 auto operation = std::make_shared<ConcreteOpType>(std::forward<Args>(args)...);
130 return add_operation(operation, name);
131 }
132
133 /**
134 * @brief Execute the pipeline with grammar rule application
135 * @param input Input data to process through the pipeline
136 * @param context Execution context containing parameters and metadata
137 * @return Processed output data
138 *
139 * Executes the complete pipeline processing workflow:
140 *
141 * 1. **Grammar Rule Application**: Searches for grammar rules that match the input
142 * data and execution context. If a matching rule is found, applies it first.
143 *
144 * 2. **Operation Chain Execution**: Executes all operations in the pipeline in
145 * the order they were added, passing output from each stage as input to the next.
146 *
147 * 3. **Type Conversion**: Handles type conversion between InputType and OutputType
148 * when they differ.
149 *
150 * The pipeline provides comprehensive error handling with operation-specific error
151 * messages that include the operation name for debugging.
152 *
153 * @throws std::runtime_error If any operation in the pipeline fails
154 */
155 output_type process(const input_type& input, const ExecutionContext& context = {})
156 {
157 input_type current_data = input;
158 ExecutionContext current_context = context;
159
160 if (auto best_rule = m_grammar->find_best_match(current_data, current_context)) {
161 if (auto rule_result = m_grammar->execute_rule(best_rule->name, current_data, current_context)) {
162 auto cast_result = safe_any_cast<input_type>(*rule_result);
163
164 if (cast_result) {
165 current_data = *cast_result.value;
166 } else {
167 MF_ERROR(
168 Journal::Component::Yantra,
169 Journal::Context::Runtime,
170 "Grammar rule '{}' returned incompatible type: {}",
171 best_rule->name,
172 cast_result.error);
173 }
174 }
175 }
176
177 for (const auto& [operation, name] : m_operations) {
178 try {
179 auto result = operation->apply_operation(current_data);
180 current_data = result;
181 } catch (const std::exception& e) {
183 Journal::Component::Yantra, Journal::Context::Runtime, std::source_location::current(),
184 "Pipeline operation '{}' failed: {}", name, e.what());
185 }
186 }
187
188 if constexpr (std::is_same_v<InputType, OutputType>) {
189 return current_data;
190 } else {
191 output_type result;
192 return result;
193 }
194 }
195
196 /**
197 * @brief Get the grammar instance
198 * @return Shared pointer to the current ComputationGrammar
199 *
200 * Provides access to the pipeline's grammar instance for adding rules,
201 * querying existing rules, or integrating with other grammar-aware components.
202 */
203 [[nodiscard]] std::shared_ptr<ComputationGrammar> get_grammar() const
204 {
205 return m_grammar;
206 }
207
208 /**
209 * @brief Set grammar instance
210 * @param grammar New grammar instance to use
211 *
212 * Replaces the current grammar with a new instance. Useful for switching
213 * between different rule sets or sharing grammars between multiple pipelines.
214 */
215 void set_grammar(std::shared_ptr<ComputationGrammar> grammar)
216 {
217 m_grammar = std::move(grammar);
218 }
219
220 /**
221 * @brief Get operation by name
222 * @tparam ConcreteOpType The expected concrete operation type
223 * @param name Name of the operation to retrieve
224 * @return Shared pointer to the operation, or nullptr if not found or wrong type
225 *
226 * Retrieves a named operation from the pipeline with automatic type casting.
227 * Returns nullptr if no operation with the given name exists or if the
228 * operation is not of the expected type.
229 *
230 * Example:
231 * ```cpp
232 * auto gain_op = pipeline->get_operation<MathematicalTransformer<>>("gain_stage");
233 * if (gain_op) {
234 * gain_op->set_parameter("gain_factor", 1.5);
235 * }
236 * ```
237 */
238 template <typename ConcreteOpType>
239 std::shared_ptr<ConcreteOpType> get_operation(const std::string& name) const
240 {
241 for (const auto& [operation, op_name] : m_operations) {
242 if (op_name == name) {
243 return std::dynamic_pointer_cast<ConcreteOpType>(operation);
244 }
245 }
246 return nullptr;
247 }
248
249 /**
250 * @brief Configure operation by name
251 * @tparam ConcreteOpType The expected concrete operation type
252 * @param name Name of the operation to configure
253 * @param configurator Function that configures the operation
254 * @return True if operation was found and configured, false otherwise
255 *
256 * Provides a safe way to configure named operations in the pipeline. The
257 * configurator function is only called if an operation with the given name
258 * exists and is of the expected type.
259 *
260 * Example:
261 * ```cpp
262 * bool configured = pipeline->configure_operation<SpectralTransformer<>>("pitch_shift",
263 * [](auto op) {
264 * op->set_parameter("pitch_ratio", 1.5);
265 * op->set_parameter("window_size", 2048);
266 * });
267 * ```
268 */
269 template <typename ConcreteOpType>
270 bool configure_operation(const std::string& name,
271 const std::function<void(std::shared_ptr<ConcreteOpType>)>& configurator)
272 {
273 for (auto& [operation, op_name] : m_operations) {
274 if (op_name == name) {
275 if (auto concrete_op = std::dynamic_pointer_cast<ConcreteOpType>(operation)) {
276 configurator(concrete_op);
277 return true;
278 }
279 }
280 }
281 return false;
282 }
283
284 /**
285 * @brief Get number of operations in pipeline
286 * @return Number of operations currently in the pipeline
287 */
288 [[nodiscard]] size_t operation_count() const
289 {
290 return m_operations.size();
291 }
292
293 /**
294 * @brief Clear all operations
295 *
296 * Removes all operations from the pipeline, leaving it empty.
297 * The grammar instance is preserved.
298 */
300 {
301 m_operations.clear();
302 }
303
304 /**
305 * @brief Get all operation names in the pipeline
306 * @return Vector of operation names in execution order
307 *
308 * Returns the names of all operations in the pipeline in the order they
309 * will be executed. Unnamed operations appear as empty strings.
310 */
311 [[nodiscard]] std::vector<std::string> get_operation_names() const
312 {
313 std::vector<std::string> names;
314 names.reserve(m_operations.size());
315 std::ranges::transform(m_operations, std::back_inserter(names),
316 [](const auto& op_pair) { return op_pair.second; });
317 return names;
318 }
319
320 /**
321 * @brief Remove operation by name
322 * @param name Name of the operation to remove
323 * @return True if operation was found and removed, false otherwise
324 *
325 * Removes the first operation with the given name from the pipeline.
326 * If multiple operations have the same name, only the first one is removed.
327 */
328 bool remove_operation(const std::string& name)
329 {
330 auto it = std::ranges::find_if(m_operations,
331 [&name](const auto& op_pair) { return op_pair.second == name; });
332
333 if (it != m_operations.end()) {
334 m_operations.erase(it);
335 return true;
336 }
337 return false;
338 }
339
340private:
341 std::shared_ptr<ComputationGrammar> m_grammar; ///< Grammar instance for rule-based operation selection
342 std::vector<std::pair<std::shared_ptr<ComputeOperation<InputType, OutputType>>, std::string>> m_operations; ///< Operations and their names in execution order
343};
344
345/**
346 * @brief Factory functions for common pipeline configurations
347 *
348 * The PipelineFactory namespace provides convenience functions for creating
349 * pre-configured pipelines for common use cases. These factories set up
350 * typical operation chains and grammar rules for specific domains.
351 */
352namespace PipelineFactory {
353
354 /**
355 * @brief Create an audio processing pipeline
356 * @tparam DataType The data type for the pipeline (defaults to std::vector<Kakshya::DataVariant>)
357 * @return Shared pointer to a configured audio processing pipeline
358 *
359 * Creates a pipeline pre-configured for audio processing workflows with
360 * typical operations for gain control, temporal effects, and spectral processing.
361 * The returned pipeline is ready to use but can be further customized.
362 *
363 * Example:
364 * ```cpp
365 * auto audio_pipeline = PipelineFactory::create_audio_pipeline<std::vector<Kakshya::DataVariant>>();
366 * auto result = audio_pipeline->process(audio_data, context);
367 * ```
368 */
369 template <ComputeData DataType = std::vector<Kakshya::DataVariant>>
370 std::shared_ptr<ComputationPipeline<DataType>> create_audio_pipeline()
371 {
372 auto pipeline = std::make_shared<ComputationPipeline<DataType>>();
373
374 // Add common audio operations
375 // pipeline->create_operation<MathematicalTransformer<DataType>>("gain");
376 // pipeline->create_operation<TemporalTransformer<DataType>>("time_effects");
377 // pipeline->create_operation<SpectralTransformer<DataType>>("frequency_effects");
378
379 return pipeline;
380 }
381
382 /**
383 * @brief Create an analysis pipeline
384 * @tparam DataType The data type for the pipeline (defaults to std::vector<Kakshya::DataVariant>)
385 * @return Shared pointer to a configured analysis pipeline
386 *
387 * Creates a pipeline pre-configured for data analysis workflows with
388 * operations for feature extraction, statistical analysis, and result processing.
389 * Suitable for machine learning preprocessing and data science workflows.
390 *
391 * Example:
392 * ```cpp
393 * auto analysis_pipeline = PipelineFactory::create_analysis_pipeline<>();
394 * auto features = analysis_pipeline->process(raw_data, analysis_context);
395 * ```
396 */
397 template <ComputeData DataType = std::vector<Kakshya::DataVariant>>
398 std::shared_ptr<ComputationPipeline<DataType>> create_analysis_pipeline()
399 {
400 auto pipeline = std::make_shared<ComputationPipeline<DataType>>();
401
402 // Add analysis operations (examples)
403 // pipeline->create_operation<FeatureExtractor<DataType>>("feature_extract");
404 // pipeline->create_operation<StandardSorter<DataType>>("sort_results");
405
406 return pipeline;
407 }
408
409} // namespace PipelineFactory
410
411/**
412 * @class GrammarAwareComputeMatrix
413 * @brief ComputeMatrix extension that integrates grammar-based operation selection
414 *
415 * The GrammarAwareComputeMatrix extends the base ComputeMatrix functionality with
416 * grammar-based rule processing. This allows for intelligent operation selection
417 * and preprocessing based on input data characteristics and execution context.
418 *
419 * Unlike pipelines that execute operations in sequence, the grammar-aware matrix
420 * can dynamically select which operations to apply based on the current data
421 * and context, making it suitable for adaptive and conditional processing workflows.
422 *
423 * ## Usage Patterns
424 *
425 * ### Basic Grammar Integration
426 * ```cpp
427 * auto grammar = std::make_shared<ComputationGrammar>();
428 * auto matrix = std::make_unique<GrammarAwareComputeMatrix>(grammar);
429 *
430 * // Grammar rules are applied before any matrix operations
431 * auto result = matrix->execute_with_grammar(input_data, context);
432 * ```
433 *
434 * ### Dynamic Operation Selection
435 * ```cpp
436 * // Grammar rules can dynamically select operations based on data properties
437 * ExecutionContext context;
438 * context.execution_metadata["processing_quality"] = std::string("high");
439 * context.execution_metadata["data_size"] = input_data.size();
440 *
441 * auto processed_data = matrix->execute_with_grammar(input_data, context);
442 * // Appropriate operations selected based on quality requirements and data size
443 * ```
444 */
445class MAYAFLUX_API GrammarAwareComputeMatrix : public ComputeMatrix {
446private:
447 std::shared_ptr<ComputationGrammar> m_grammar; ///< Grammar instance for rule-based operation selection
448
449public:
450 /**
451 * @brief Constructor with optional grammar
452 * @param grammar Shared pointer to ComputationGrammar instance (creates new if nullptr)
453 *
454 * Creates a grammar-aware compute matrix with the specified grammar instance.
455 * If no grammar is provided, creates a new empty grammar that can be populated
456 * with rules later.
457 */
458 explicit GrammarAwareComputeMatrix(std::shared_ptr<ComputationGrammar> grammar = nullptr)
459 : m_grammar(grammar ? std::move(grammar) : std::make_shared<ComputationGrammar>())
460 {
461 }
462
463 /**
464 * @brief Execute operations with grammar rule pre-processing
465 * @tparam InputType The input data type
466 * @param input Input data to process
467 * @param context Execution context containing parameters and metadata
468 * @return Processed input data after grammar rule application
469 *
470 * Applies grammar rules to the input data before any matrix operations.
471 * This allows for intelligent preprocessing, operation selection, and
472 * parameter configuration based on the input characteristics and context.
473 *
474 * The process:
475 * 1. Wraps input data in Datum structure
476 * 2. Searches for matching grammar rules
477 * 3. Applies the best matching rule if found
478 * 4. Returns processed data or original data if no rules match
479 *
480 * @note This method focuses on grammar rule application. Use the base
481 * ComputeMatrix methods for actual operation execution.
482 */
483 template <ComputeData InputType>
485 {
486 static_assert(sizeof(Datum<InputType>) > 0, "Datum incomplete");
487 Datum<InputType> current = input;
488
489 if (auto best_rule = m_grammar->find_best_match(current, context)) {
490 if (auto rule_result = m_grammar->execute_rule(best_rule->name, current, context)) {
491 auto cast_result = safe_any_cast<Datum<InputType>>(*rule_result);
492 if (cast_result) {
493 current = *cast_result.value;
494 } else {
495 MF_WARN(
496 Journal::Component::Yantra,
497 Journal::Context::Runtime,
498 "Grammar rule '{}' returned incompatible type: {}",
499 best_rule->name,
500 cast_result.error);
501 }
502 }
503 }
504
505 return current;
506 }
507
508 /**
509 * @brief Execute a named grammar rule explicitly by name.
510 * @tparam InputType The input data type.
511 * @param rule_name Name of the grammar rule to execute.
512 * @param input Input data to process.
513 * @param context Execution context containing parameters and metadata.
514 * @return Processed data after rule application, or original input if the
515 * rule is not found or returns an incompatible type.
516 */
517 template <ComputeData InputType>
519 const std::string& rule_name,
520 const Datum<InputType>& input,
521 const ExecutionContext& context = {})
522 {
523 Datum<InputType> current = input;
524 if (auto rule_result = m_grammar->execute_rule(rule_name, current, context)) {
525 auto cast_result = safe_any_cast<Datum<InputType>>(*rule_result);
526 if (cast_result) {
527 current = *cast_result.value;
528 } else {
529 MF_WARN(
530 Journal::Component::Yantra,
531 Journal::Context::Runtime,
532 "Grammar rule '{}' returned incompatible type: {}",
533 rule_name,
534 cast_result.error);
535 }
536 } else {
537 MF_WARN(
538 Journal::Component::Yantra,
539 Journal::Context::Runtime,
540 "Grammar rule '{}' not found",
541 rule_name);
542 }
543 return current;
544 }
545
546 /**
547 * @brief Get the grammar instance
548 * @return Shared pointer to the current ComputationGrammar
549 *
550 * Provides access to the grammar instance for adding rules, querying existing
551 * rules, or integrating with other grammar-aware components.
552 */
553 std::shared_ptr<ComputationGrammar> get_grammar() const
554 {
555 return m_grammar;
556 }
557
558 /**
559 * @brief Set grammar instance
560 * @param grammar New grammar instance to use
561 *
562 * Replaces the current grammar with a new instance. Useful for switching
563 * between different rule sets or sharing grammars between multiple components.
564 */
565 void set_grammar(std::shared_ptr<ComputationGrammar> grammar)
566 {
567 m_grammar = std::move(grammar);
568 }
569
570 /**
571 * @brief Add a grammar rule directly to the matrix
572 * @param rule Rule to add to the grammar
573 *
574 * Convenience method to add rules directly to the matrix's grammar without
575 * needing to access the grammar instance separately. Useful for quick
576 * rule addition during matrix configuration.
577 */
579 {
580 m_grammar->add_rule(std::move(rule));
581 }
582
583 /**
584 * @brief Create a rule builder for this matrix's grammar
585 * @param name Unique name for the rule
586 * @return RuleBuilder instance for method chaining
587 *
588 * Provides direct access to the grammar's rule building interface,
589 * allowing for fluent rule creation without explicit grammar access.
590 *
591 * Example:
592 * ```cpp
593 * matrix->create_grammar_rule("auto_gain")
594 * .with_context(ComputationContext::TEMPORAL)
595 * .matches_type<std::vector<double>>()
596 * .executes([](const auto& input, const auto& ctx) { return input; })
597 * .build();
598 * ```
599 */
601 {
602 return m_grammar->create_rule(name);
603 }
604};
605
606} // namespace MayaFlux::Yantra
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
Fluent interface for building rules with method chaining.
Core grammar system for rule-based computation in Maya Flux.
std::shared_ptr< ConcreteOpType > get_operation(const std::string &name) const
Get operation by name.
ComputationPipeline & create_operation(const std::string &name="", Args &&... args)
Create and add operation by type.
bool remove_operation(const std::string &name)
Remove operation by name.
std::shared_ptr< ComputationGrammar > get_grammar() const
Get the grammar instance.
void clear_operations()
Clear all operations.
size_t operation_count() const
Get number of operations in pipeline.
void set_grammar(std::shared_ptr< ComputationGrammar > grammar)
Set grammar instance.
output_type process(const input_type &input, const ExecutionContext &context={})
Execute the pipeline with grammar rule application.
std::vector< std::pair< std::shared_ptr< ComputeOperation< InputType, OutputType > >, std::string > > m_operations
Operations and their names in execution order.
std::shared_ptr< ComputationGrammar > m_grammar
Grammar instance for rule-based operation selection.
ComputationPipeline(std::shared_ptr< ComputationGrammar > grammar=nullptr)
Constructor with optional grammar.
std::vector< std::string > get_operation_names() const
Get all operation names in the pipeline.
bool configure_operation(const std::string &name, const std::function< void(std::shared_ptr< ConcreteOpType >)> &configurator)
Configure operation by name.
ComputationPipeline & add_operation(std::shared_ptr< ConcreteOpType > operation, const std::string &name="")
Add a concrete operation instance to the pipeline.
Pipeline that uses grammar rules for operation composition.
Local execution orchestrator for computational operations.
Base interface for all computational operations in the processing pipeline.
Datum< InputType > execute_with_grammar(const std::string &rule_name, const Datum< InputType > &input, const ExecutionContext &context={})
Execute a named grammar rule explicitly by name.
std::shared_ptr< ComputationGrammar > m_grammar
Grammar instance for rule-based operation selection.
void add_grammar_rule(ComputationGrammar::Rule rule)
Add a grammar rule directly to the matrix.
std::shared_ptr< ComputationGrammar > get_grammar() const
Get the grammar instance.
GrammarAwareComputeMatrix(std::shared_ptr< ComputationGrammar > grammar=nullptr)
Constructor with optional grammar.
void set_grammar(std::shared_ptr< ComputationGrammar > grammar)
Set grammar instance.
Datum< InputType > execute_with_grammar(const Datum< InputType > &input, const ExecutionContext &context={})
Execute operations with grammar rule pre-processing.
ComputationGrammar::RuleBuilder create_grammar_rule(const std::string &name)
Create a rule builder for this matrix's grammar.
ComputeMatrix extension that integrates grammar-based operation selection.
void error_rethrow(Component component, Context context, std::source_location location=std::source_location::current(), std::string_view additional_context="")
Catch and log an exception, then rethrow it.
std::shared_ptr< ComputationPipeline< DataType > > create_audio_pipeline()
Create an audio processing pipeline.
std::shared_ptr< ComputationPipeline< DataType > > create_analysis_pipeline()
Create an analysis pipeline.
Represents a computation rule with matching and execution logic.
Input/Output container for computation pipeline data flow with structure preservation.
Definition DataIO.hpp:24
Context information controlling how a compute operation executes.