MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ComputeGrammar.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::Yantra {
6
7/**
8 * @class ComputationGrammar
9 * @brief Core grammar system for rule-based computation in Maya Flux
10 *
11 * The ComputationGrammar provides a powerful, declarative system for defining how
12 * computational operations should be applied based on input data characteristics,
13 * execution context, and user-defined rules. This enables intelligent, adaptive
14 * computation that can select appropriate operations dynamically.
15 *
16 * ## Core Concepts
17 *
18 * **Rules**: Define when and how operations should be applied. Each rule contains:
19 * - Matching logic to determine if the rule applies to given input
20 * - Execution logic that performs the actual computation
21 * - Metadata for organization, prioritization, and optimization
22 *
23 * **Contexts**: Categorize rules by computational domain (temporal, spectral, etc.)
24 * for efficient lookup and logical organization.
25 *
26 * **Priority System**: Higher priority rules are evaluated first, allowing for
27 * hierarchical decision making and exception handling.
28 *
29 * ## Usage Patterns
30 *
31 * ### Simple Rule Creation
32 * ```cpp
33 * ComputationGrammar grammar;
34 *
35 * // Add a rule for mathematical transformations on double vectors
36 * grammar.create_rule("gain_amplification")
37 * .with_context(ComputationContext::TEMPORAL)
38 * .with_priority(100)
39 * .matches_type<std::vector<double>>()
40 * .executes([](const std::any& input, const ExecutionContext& ctx) {
41 * // Custom transformation logic
42 * return input; // simplified
43 * })
44 * .build();
45 * ```
46 *
47 * ### Complex Matcher Combinations
48 * ```cpp
49 * auto complex_matcher = UniversalMatcher::combine_and({
50 * UniversalMatcher::create_type_matcher<std::vector<Kakshya::DataVariant>>(),
51 * UniversalMatcher::create_context_matcher(ComputationContext::SPECTRAL),
52 * UniversalMatcher::create_parameter_matcher("frequency_range", std::string("audio"))
53 * });
54 *
55 * grammar.create_rule("spectral_filter")
56 * .matches_custom(complex_matcher)
57 * .executes([](const std::any& input, const ExecutionContext& ctx) {
58 * // Spectral filtering logic
59 * return input;
60 * })
61 * .build();
62 * ```
63 *
64 * ### Operation Integration
65 * ```cpp
66 * // Create rule that automatically applies MathematicalTransformer
67 * grammar.add_operation_rule<MathematicalTransformer<>>(
68 * "auto_normalize",
69 * ComputationContext::MATHEMATICAL,
70 * UniversalMatcher::create_type_matcher<std::vector<Kakshya::DataVariant>>(),
71 * {{"operation", std::string("normalize")}, {"target_peak", 1.0}},
72 * 75 // priority
73 * );
74 * ```
75 */
76class MAYAFLUX_API ComputationGrammar {
77public:
78 /**
79 * @struct Rule
80 * @brief Represents a computation rule with matching and execution logic
81 *
82 * Rules are the fundamental building blocks of the grammar system. Each rule
83 * encapsulates the logic for determining when it should be applied (matcher)
84 * and what computation it should perform (executor), along with metadata
85 * for organization and optimization.
86 */
87 struct MAYAFLUX_API Rule {
88 /**
89 * @brief Type alias for matcher functions used in computation rules
90 */
91 using Executor = std::function<std::any(const std::any&, const ExecutionContext&)>;
92
93 std::string name; ///< Unique identifier for this rule
94 std::string description; ///< Human-readable description of what the rule does
95 ComputationContext context {}; ///< Computational context this rule operates in
96 int priority = 0; ///< Execution priority (higher values evaluated first)
97
98 UniversalMatcher::MatcherFunc matcher; ///< Function that determines if rule applies
99 Executor executor; ///< Function that performs the computation
100
101 std::vector<std::string> dependencies; ///< Names of rules that must execute before this one
102 std::unordered_map<std::string, std::any> default_parameters; ///< Default parameters for the rule's operation
103
104 std::chrono::milliseconds max_execution_time { 0 }; ///< Maximum allowed execution time (0 = unlimited)
105 ExecutionMode preferred_execution_mode = ExecutionMode::SYNC; ///< Preferred execution mode for this rule
106
107 std::type_index target_operation_type = std::type_index(typeid(void)); ///< Type of operation this rule creates (for type-based queries)
108
109 std::vector<std::string> tags; ///< Arbitrary tags for categorization and search
110 };
111
112 /**
113 * @brief Add a rule to the grammar
114 * @param rule Rule to add to the grammar system
115 *
116 * Rules are automatically sorted by priority (highest first) and indexed by context
117 * for efficient lookup. The rule's name must be unique within the grammar.
118 *
119 * @note Rules with higher priority values are evaluated first during matching
120 */
121 void add_rule(Rule rule)
122 {
123 std::string rule_name = rule.name;
124 ComputationContext rule_context = rule.context;
125
126 auto insert_pos = std::ranges::upper_bound(m_rules, rule,
127 [](const Rule& a, const Rule& b) { return a.priority > b.priority; });
128 m_rules.insert(insert_pos, std::move(rule));
129
130 m_context_index[rule_context].push_back(rule_name);
131 }
132
133 /**
134 * @brief Find the best matching rule for the given input
135 * @param input Input data to match against rules
136 * @param context Execution context containing parameters and metadata
137 * @return First rule that matches the input/context, or nullopt if no match
138 *
139 * Rules are evaluated in priority order (highest first). The first rule whose
140 * matcher function returns true is considered the best match. This allows for
141 * hierarchical decision making where specific rules can override general ones.
142 *
143 * @note The matcher function receives both the input data and execution context,
144 * allowing for complex matching logic based on data type, content, and context
145 */
146 std::optional<Rule> find_best_match(const std::any& input, const ExecutionContext& context) const
147 {
148 for (const auto& rule : m_rules) {
149 if (rule.matcher(input, context)) {
150 return rule;
151 }
152 }
153 return std::nullopt;
154 }
155
156 /**
157 * @brief Execute a specific rule by name
158 * @param rule_name Name of the rule to execute
159 * @param input Input data for the rule's executor
160 * @param context Execution context containing parameters and metadata
161 * @return Result of rule execution, or nullopt if rule not found or doesn't match
162 *
163 * Finds the named rule and executes it if its matcher function returns true
164 * for the given input and context. This allows for explicit rule invocation
165 * when the specific rule to apply is known.
166 *
167 * @note The rule's matcher is still evaluated even when invoked by name,
168 * ensuring that rules maintain their matching contracts
169 */
170 std::optional<std::any> execute_rule(const std::string& rule_name,
171 const std::any& input,
172 const ExecutionContext& context) const
173 {
174 auto it = std::ranges::find_if(m_rules,
175 [&rule_name](const Rule& rule) { return rule.name == rule_name; });
176
177 if (it != m_rules.end() && it->matcher(input, context)) {
178 return it->executor(input, context);
179 }
180 return std::nullopt;
181 }
182
183 /**
184 * @brief Get all rule names for a specific computation context
185 * @param context The computation context to query
186 * @return Vector of rule names that belong to the specified context
187 *
188 * Useful for discovering what rules are available for a particular computational
189 * domain (e.g., all temporal processing rules) or for building context-specific
190 * processing pipelines.
191 */
192 std::vector<std::string> get_rules_by_context(ComputationContext context) const
193 {
194 auto it = m_context_index.find(context);
195 return it != m_context_index.end() ? it->second : std::vector<std::string> {};
196 }
197
198 /**
199 * @brief Get rules that target a specific operation type
200 * @tparam OperationType The operation type to search for
201 * @return Vector of rule names that create or target the specified operation type
202 *
203 * Enables type-based rule discovery, useful for finding all rules that can
204 * create instances of a particular operation type or for verifying rule coverage
205 * for specific operation types.
206 *
207 * Example:
208 * ```cpp
209 * auto math_rules = grammar.get_rules_for_operation_type<MathematicalTransformer<>>();
210 * // Returns names of all rules that create MathematicalTransformer instances
211 * ```
212 */
213 template <typename OperationType>
214 std::vector<std::string> get_rules_for_operation_type() const
215 {
216 std::vector<std::string> matching_rules;
217 auto target_type = std::type_index(typeid(OperationType));
218
219 for (const auto& rule : m_rules) {
220 if (rule.target_operation_type == target_type) {
221 matching_rules.push_back(rule.name);
222 }
223 }
224 return matching_rules;
225 }
226
227 /**
228 * @brief Helper to add concrete operation rules with automatic executor generation
229 * @tparam ConcreteOpType The concrete operation type to instantiate
230 * @tparam OpArgs Constructor argument types for the operation
231 * @param rule_name Unique name for this rule
232 * @param context Computation context for the rule
233 * @param matcher Matcher function to determine when rule applies
234 * @param op_parameters Parameters to configure the operation instance
235 * @param priority Execution priority (default: 50)
236 * @param op_args Constructor arguments for the operation
237 *
238 * Creates a rule that automatically instantiates and configures a concrete operation
239 * type when matched. This is the preferred way to integrate existing operations
240 * into the grammar system, as it handles type safety and parameter application
241 * automatically.
242 *
243 * The generated executor:
244 * 1. Creates an instance of ConcreteOpType with the provided constructor arguments
245 * 2. Applies the op_parameters using set_parameter()
246 * 3. Applies additional parameters from the execution context
247 * 4. Executes the operation on the input data
248 *
249 * Example:
250 * ```cpp
251 * grammar.add_operation_rule<SpectralTransformer<>>(
252 * "pitch_shift_rule",
253 * ComputationContext::SPECTRAL,
254 * UniversalMatcher::create_type_matcher<std::vector<Kakshya::DataVariant>>(),
255 * {{"operation", std::string("pitch_shift")}, {"pitch_ratio", 1.5}},
256 * 80 // priority
257 * );
258 * ```
259 */
260 template <typename ConcreteOpType, typename... OpArgs>
261 void add_operation_rule(const std::string& rule_name,
262 ComputationContext context,
264 const std::unordered_map<std::string, std::any>& op_parameters = {},
265 int priority = 50,
266 OpArgs&&... op_args)
267 {
268 Rule rule;
269 rule.name = rule_name;
270 rule.context = context;
271 rule.priority = priority;
272 rule.matcher = std::move(matcher);
273 rule.target_operation_type = std::type_index(typeid(ConcreteOpType));
274
275 auto captured_args = std::make_tuple(std::forward<OpArgs>(op_args)...);
276
277 rule.executor = [op_parameters, captured_args = std::move(captured_args)](const std::any& input, const ExecutionContext& ctx) -> std::any {
278 auto operation = std::apply([&op_parameters](auto&&... args) {
279 return create_configured_operation<ConcreteOpType>(op_parameters, std::forward<decltype(args)>(args)...);
280 },
281 captured_args);
282
283 apply_context_parameters(operation, ctx);
284
285 auto typed_input = safe_any_cast_or_throw<DataIO>(input);
286 return operation->apply_operation(typed_input);
287 };
288
289 add_rule(std::move(rule));
290 }
291
292 /**
293 * @class RuleBuilder
294 * @brief Fluent interface for building rules with method chaining
295 *
296 * The RuleBuilder provides a clean, readable way to construct complex rules
297 * using method chaining. This pattern makes rule creation more expressive
298 * and helps catch configuration errors at compile time.
299 *
300 * Example usage:
301 * ```cpp
302 * grammar.create_rule("complex_temporal_rule")
303 * .with_context(ComputationContext::TEMPORAL)
304 * .with_priority(75)
305 * .with_description("Applies gain when signal is quiet")
306 * .matches_type<std::vector<double>>()
307 * .targets_operation<MathematicalTransformer<>>()
308 * .with_tags({"audio", "gain", "dynamic"})
309 * .executes([](const std::any& input, const ExecutionContext& ctx) {
310 * // Custom logic here
311 * return input;
312 * })
313 * .build();
314 * ```
315 */
316 class MAYAFLUX_API RuleBuilder {
317 private:
318 Rule m_rule; ///< Rule being constructed
319 ComputationGrammar* m_grammar; ///< Reference to parent grammar
320
321 public:
322 /**
323 * @brief Constructs a RuleBuilder for the specified grammar
324 * @param grammar Parent grammar that will receive the built rule
325 * @param name Unique name for the rule being built
326 */
327 explicit RuleBuilder(ComputationGrammar* grammar, std::string name)
328 : m_grammar(grammar)
329 {
330 m_rule.name = std::move(name);
331 }
332
333 /**
334 * @brief Sets the computation context for this rule
335 * @param context The computational context (temporal, spectral, etc.)
336 * @return Reference to this builder for method chaining
337 */
339 {
340 m_rule.context = context;
341 return *this;
342 }
343
344 /**
345 * @brief Sets the execution priority for this rule
346 * @param priority Priority value (higher values evaluated first)
347 * @return Reference to this builder for method chaining
348 */
350 {
351 m_rule.priority = priority;
352 return *this;
353 }
354
355 /**
356 * @brief Sets a human-readable description for this rule
357 * @param description Description of what the rule does
358 * @return Reference to this builder for method chaining
359 */
360 RuleBuilder& with_description(std::string description)
361 {
362 m_rule.description = std::move(description);
363 return *this;
364 }
365
366 /**
367 * @brief Sets the matcher to check for a specific data type
368 * @tparam DataType The ComputeData type to match against
369 * @return Reference to this builder for method chaining
370 *
371 * Creates a type-based matcher that returns true when the input
372 * data is of the specified type. This is the most common matching
373 * strategy for type-specific operations.
374 */
375 template <ComputeData DataType>
377 {
378 m_rule.matcher = UniversalMatcher::create_type_matcher<DataType>();
379 return *this;
380 }
381
382 /**
383 * @brief Sets a custom matcher function
384 * @param matcher Custom matcher function
385 * @return Reference to this builder for method chaining
386 *
387 * Allows for complex matching logic based on data content, context
388 * parameters, or combinations of multiple criteria. Use this when
389 * simple type matching is insufficient.
390 */
392 {
393 m_rule.matcher = std::move(matcher);
394 return *this;
395 }
396
397 /**
398 * @brief Sets the executor function for this rule
399 * @tparam Func Function type (usually a lambda)
400 * @param executor Function that performs the computation
401 * @return Reference to this builder for method chaining
402 *
403 * The executor function receives the input data and execution context,
404 * and returns the result of the computation. This is where the actual
405 * work of the rule is performed.
406 */
407 template <typename Func>
408 RuleBuilder& executes(Func&& executor)
409 {
410 m_rule.executor = [func = std::forward<Func>(executor)](const std::any& input, const ExecutionContext& ctx) -> std::any {
411 return func(input, ctx);
412 };
413 return *this;
414 }
415
416 /**
417 * @brief Sets the target operation type for this rule
418 * @tparam OperationType The operation type this rule creates or targets
419 * @return Reference to this builder for method chaining
420 *
421 * Used for type-based rule queries and validation. Helps organize
422 * rules by the types of operations they create or work with.
423 */
424 template <typename OperationType>
426 {
427 m_rule.target_operation_type = std::type_index(typeid(OperationType));
428 return *this;
429 }
430
431 /**
432 * @brief Sets arbitrary tags for this rule
433 * @param tags Vector of tag strings for categorization
434 * @return Reference to this builder for method chaining
435 *
436 * Tags provide flexible categorization and search capabilities.
437 * Useful for organizing rules by domain, use case, or other
438 * arbitrary criteria.
439 */
440 RuleBuilder& with_tags(std::vector<std::string> tags)
441 {
442 m_rule.tags = std::move(tags);
443 return *this;
444 }
445
446 /**
447 * @brief Finalizes and adds the rule to the grammar
448 *
449 * This method must be called to complete rule construction.
450 * The built rule is added to the parent grammar and sorted
451 * by priority for efficient matching.
452 *
453 * @note After calling build(), this RuleBuilder should not be used again
454 */
455 void build()
456 {
457 m_grammar->add_rule(std::move(m_rule));
458 }
459 };
460
461 /**
462 * @brief Create a rule builder for fluent rule construction
463 * @param name Unique name for the rule
464 * @return RuleBuilder instance for method chaining
465 *
466 * This is the entry point for the fluent rule building interface.
467 * Returns a RuleBuilder that can be used to configure and build
468 * a rule using method chaining.
469 *
470 * Example:
471 * ```cpp
472 * auto builder = grammar.create_rule("my_rule");
473 * builder.with_context(ComputationContext::MATHEMATICAL)
474 * .matches_type<std::vector<double>>()
475 * .executes([](const auto& input, const auto& ctx) { return input; })
476 * .build();
477 * ```
478 */
479 RuleBuilder create_rule(const std::string& name)
480 {
481 return RuleBuilder(this, name);
482 }
483
484 /**
485 * @brief Get the total number of rules in the grammar
486 * @return Number of rules currently registered
487 */
488 [[nodiscard]] size_t get_rule_count() const { return m_rules.size(); }
489
490 /**
491 * @brief Get all rule names in the grammar
492 * @return Vector of all rule names, ordered by priority
493 */
494 [[nodiscard]] std::vector<std::string> get_all_rule_names() const
495 {
496 std::vector<std::string> names;
497 names.reserve(m_rules.size());
498 std::ranges::transform(m_rules, std::back_inserter(names),
499 [](const Rule& rule) { return rule.name; });
500 return names;
501 }
502
503 /**
504 * @brief Check if a rule with the given name exists
505 * @param rule_name Name to check
506 * @return True if rule exists, false otherwise
507 */
508 [[nodiscard]] bool has_rule(const std::string& rule_name) const
509 {
510 return std::ranges::any_of(m_rules,
511 [&rule_name](const Rule& rule) { return rule.name == rule_name; });
512 }
513
514 /**
515 * @brief Remove a rule by name
516 * @param rule_name Name of rule to remove
517 * @return True if rule was removed, false if not found
518 *
519 * Removes the rule from both the main rule list and the context index.
520 * This is useful for dynamic rule management and grammar updates.
521 */
522 bool remove_rule(const std::string& rule_name)
523 {
524 auto it = std::ranges::find_if(m_rules,
525 [&rule_name](const Rule& rule) { return rule.name == rule_name; });
526
527 if (it != m_rules.end()) {
528 ComputationContext context = it->context;
529 m_rules.erase(it);
530
531 auto& context_rules = m_context_index[context];
532 std::erase_if(context_rules,
533 [&](const std::string& name) {
534 return name == rule_name;
535 });
536
537 return true;
538 }
539 return false;
540 }
541
542 /**
543 * @brief Clear all rules from the grammar
544 *
545 * Removes all rules and clears all indices. Useful for resetting
546 * the grammar to a clean state or for testing scenarios.
547 */
549 {
550 m_rules.clear();
551 m_context_index.clear();
552 }
553
554private:
555 std::vector<Rule> m_rules; ///< All rules sorted by priority (highest first)
556 std::unordered_map<ComputationContext, std::vector<std::string>> m_context_index; ///< Index of rule names by context for fast lookup
557};
558
559} // namespace MayaFlux::Yantra
size_t a
size_t b
void build()
Finalizes and adds the rule to the grammar.
RuleBuilder(ComputationGrammar *grammar, std::string name)
Constructs a RuleBuilder for the specified grammar.
RuleBuilder & with_context(ComputationContext context)
Sets the computation context for this rule.
RuleBuilder & matches_type()
Sets the matcher to check for a specific data type.
ComputationGrammar * m_grammar
Reference to parent grammar.
RuleBuilder & executes(Func &&executor)
Sets the executor function for this rule.
RuleBuilder & with_tags(std::vector< std::string > tags)
Sets arbitrary tags for this rule.
RuleBuilder & matches_custom(UniversalMatcher::MatcherFunc matcher)
Sets a custom matcher function.
RuleBuilder & with_priority(int priority)
Sets the execution priority for this rule.
RuleBuilder & with_description(std::string description)
Sets a human-readable description for this rule.
RuleBuilder & targets_operation()
Sets the target operation type for this rule.
Fluent interface for building rules with method chaining.
bool remove_rule(const std::string &rule_name)
Remove a rule by name.
RuleBuilder create_rule(const std::string &name)
Create a rule builder for fluent rule construction.
std::unordered_map< ComputationContext, std::vector< std::string > > m_context_index
Index of rule names by context for fast lookup.
std::optional< Rule > find_best_match(const std::any &input, const ExecutionContext &context) const
Find the best matching rule for the given input.
std::vector< std::string > get_rules_for_operation_type() const
Get rules that target a specific operation type.
size_t get_rule_count() const
Get the total number of rules in the grammar.
void clear_all_rules()
Clear all rules from the grammar.
std::vector< std::string > get_all_rule_names() const
Get all rule names in the grammar.
std::vector< std::string > get_rules_by_context(ComputationContext context) const
Get all rule names for a specific computation context.
bool has_rule(const std::string &rule_name) const
Check if a rule with the given name exists.
void add_operation_rule(const std::string &rule_name, ComputationContext context, UniversalMatcher::MatcherFunc matcher, const std::unordered_map< std::string, std::any > &op_parameters={}, int priority=50, OpArgs &&... op_args)
Helper to add concrete operation rules with automatic executor generation.
std::optional< std::any > execute_rule(const std::string &rule_name, const std::any &input, const ExecutionContext &context) const
Execute a specific rule by name.
void add_rule(Rule rule)
Add a rule to the grammar.
std::vector< Rule > m_rules
All rules sorted by priority (highest first)
Core grammar system for rule-based computation in Maya Flux.
std::function< bool(const std::any &, const ExecutionContext &)> MatcherFunc
ComputationContext
Defines the computational contexts in which rules can be applied.
OperationType
Operation categories for organization and discovery.
ExecutionMode
Execution paradigms for operations.
ComputationContext context
Computational context this rule operates in.
std::type_index target_operation_type
Type of operation this rule creates (for type-based queries)
int priority
Execution priority (higher values evaluated first)
std::unordered_map< std::string, std::any > default_parameters
Default parameters for the rule's operation.
Executor executor
Function that performs the computation.
std::string description
Human-readable description of what the rule does.
std::string name
Unique identifier for this rule.
std::vector< std::string > tags
Arbitrary tags for categorization and search.
std::vector< std::string > dependencies
Names of rules that must execute before this one.
UniversalMatcher::MatcherFunc matcher
Function that determines if rule applies.
std::function< std::any(const std::any &, const ExecutionContext &)> Executor
Type alias for matcher functions used in computation rules.
Represents a computation rule with matching and execution logic.
Context information controlling how a compute operation executes.