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