conjure_cp_core/rule_engine/mod.rs
1/// This procedural macro registers a decorated function with `conjure_cp_rules`' global registry, and
2/// adds the rule to one or more `RuleSet`'s.
3///
4/// It may be used in any downstream crate.
5/// For more information on registration, see the [`inventory`](https://docs.rs/inventory/latest/inventory/) crate.
6///
7/// <hr>
8///
9/// Functions must have the signature `fn(&Expr) -> ApplicationResult`.
10/// The created rule will have the same name as the function.
11///
12/// Intermediary static variables are created to allow for the decentralized registry, with the prefix `CONJURE_GEN_`.
13/// Please ensure that other variable names in the same scope do not conflict with these.
14///
15/// This macro must decorate a function with the given signature.
16/// As arguments, it excepts a tuple of 2-tuples in the format:
17/// `((<RuleSet name>, <Priority in RuleSet>), ...)`
18///
19/// <hr>
20///
21/// For example:
22/// ```rust
23/// use conjure_cp_core::ast::Expression;
24/// use conjure_cp_core::ast::SymbolTable;
25/// use conjure_cp_core::rule_engine::{ApplicationError, ApplicationResult, RuleEffect};
26/// use conjure_cp_core::rule_engine::register_rule;
27///
28/// #[register_rule("RuleSetName", 10)]
29/// fn identity(expr: &Expression, symbols: &SymbolTable) -> ApplicationResult {
30/// Ok(RuleEffect::pure(expr.clone()))
31/// }
32/// ```
33pub use conjure_cp_rule_macros::register_rule;
34
35/// This procedural macro registers a rule set with the global registry.
36/// It may be used in any downstream crate.
37///
38/// For more information on registration, see the [`inventory`](https://docs.rs/inventory/latest/inventory/) crate.
39///
40/// This macro uses the following syntax:
41///
42/// ```text
43/// register_rule_set!(<RuleSet name>, (<DependencyRuleSet1>, <DependencyRuleSet2>, ...), <SolverFamily>);
44/// ```
45///
46/// # Example
47///
48/// Register a rule set with no dependencies:
49///
50/// ```rust
51/// use conjure_cp_core::rule_engine::register_rule_set;
52/// register_rule_set!("MyRuleSet");
53/// ```
54///
55/// Register a rule set with dependencies:
56///
57/// ```rust
58/// use conjure_cp_core::rule_engine::register_rule_set;
59/// register_rule_set!("MyRuleSet", ("DependencyRuleSet", "AnotherRuleSet"));
60/// ```
61///
62/// Register a rule set for a specific solver family or families:
63///
64/// ```rust
65/// use conjure_cp_core::rule_engine::register_rule_set;
66/// use conjure_cp_core::settings::SolverFamily;
67/// register_rule_set!("MyRuleSet", (), |f: &SolverFamily| matches!(f, SolverFamily::Minion));
68/// register_rule_set!("AnotherRuleSet", (), |f: &SolverFamily| matches!(f, SolverFamily::Minion | SolverFamily::Sat));
69/// ```
70#[doc(inline)]
71pub use conjure_cp_rule_macros::register_rule_set;
72pub use resolve_rules::{RuleData, get_rules, get_rules_grouped, resolve_rule_sets};
73pub use rewrite::rewrite_model;
74pub use rewriter_common::RewriteError;
75pub use rule::{
76 ApplicationError, ApplicationResult, AtomKind, Rule, RuleEffect, RuleFailureInvalidation,
77 RuleFn, RulePrefilter,
78};
79pub use rule_set::RuleSet;
80
81mod expression_zipper;
82
83#[doc(hidden)]
84pub use expression_zipper::ExpressionZipper;
85
86use crate::{
87 Model,
88 settings::{Rewriter, SolverFamily},
89};
90
91mod resolve_rules;
92mod rewrite;
93mod rewriter_common;
94mod rule;
95mod rule_set;
96
97inventory::collect!(&'static Rule<'static>);
98
99inventory::collect!(&'static RuleSet<'static>);
100
101pub mod _dependencies {
102 pub use inventory;
103}
104
105/// Returns a copied `Vec` of all rules registered with the `register_rule` macro.
106///
107/// Rules are not guaranteed to be in any particular order.
108///
109/// # Example
110/// ```rust
111/// # use conjure_cp_core::rule_engine::{ApplicationResult, RuleEffect, get_all_rules};
112/// # use conjure_cp_core::ast::Expression;
113/// # use conjure_cp_core::ast::SymbolTable;
114/// # use conjure_cp_core::rule_engine::register_rule;
115///
116/// #[register_rule]
117/// fn identity(expr: &Expression, symbols: &SymbolTable) -> ApplicationResult {
118/// Ok(RuleEffect::pure(expr.clone()))
119/// }
120///
121/// fn main() {
122/// println!("Rules: {:?}", get_all_rules());
123/// }
124/// ```
125///
126/// This will print (if no other rules are registered):
127/// ```text
128/// Rules: [Rule { name: "identity", application: MEM }]
129/// ```
130/// Where `MEM` is the memory address of the `identity` function.
131pub fn get_all_rules() -> Vec<&'static Rule<'static>> {
132 inventory::iter::<&'static Rule<'static>>
133 .into_iter()
134 .copied()
135 .collect()
136}
137
138/// Get a rule by name.
139/// Returns the rule with the given name or None if it doesn't exist.
140///
141/// # Example
142/// ```rust
143/// use conjure_cp_core::rule_engine::register_rule;
144/// use conjure_cp_core::rule_engine::{Rule, ApplicationResult, RuleEffect, get_rule_by_name};
145/// use conjure_cp_core::ast::Expression;
146/// use conjure_cp_core::ast::SymbolTable;
147///
148/// #[register_rule]
149/// fn identity(expr: &Expression, symbols: &SymbolTable) -> ApplicationResult {
150/// Ok(RuleEffect::pure(expr.clone()))
151/// }
152///
153/// fn main() {
154/// println!("Rule: {:?}", get_rule_by_name("identity"));
155/// }
156/// ```
157///
158/// This will print:
159/// ```text
160/// Rule: Some(Rule { name: "identity", application: MEM })
161/// ```
162pub fn get_rule_by_name(name: &str) -> Option<&'static Rule<'static>> {
163 get_all_rules()
164 .iter()
165 .find(|rule| rule.name == name)
166 .copied()
167}
168
169/// Get all rule sets
170/// Returns a `Vec` of static references to all rule sets registered with the `register_rule_set` macro.
171/// Rule sets are not guaranteed to be in any particular order.
172///
173/// # Example
174/// ```rust
175/// use conjure_cp_core::rule_engine::register_rule_set;
176/// use conjure_cp_core::rule_engine::get_all_rule_sets;
177///
178/// register_rule_set!("MyRuleSet", ("AnotherRuleSet"));
179/// register_rule_set!("AnotherRuleSet", ());
180///
181/// println!("Rule sets: {:?}", get_all_rule_sets());
182/// ```
183///
184/// This will print (if no other rule sets are registered):
185/// ```text
186/// Rule sets: [
187/// RuleSet { name: "MyRuleSet", rules: OnceLock { state: Uninitialized }, dependencies: ["AnotherRuleSet"] },
188/// RuleSet { name: "AnotherRuleSet", rules: OnceLock { state: Uninitialized }, dependencies: [] }
189/// ]
190/// ```
191///
192pub fn get_all_rule_sets() -> Vec<&'static RuleSet<'static>> {
193 inventory::iter::<&'static RuleSet<'static>>
194 .into_iter()
195 .copied()
196 .collect()
197}
198
199/// Rewrites a model using the supplied rewriter configuration.
200pub fn rewrite_model_with_configured_rewriter<'a>(
201 model: Model,
202 rule_sets: &Vec<&'a RuleSet<'a>>,
203 configured_rewriter: Rewriter,
204) -> Result<Model, RewriteError> {
205 match configured_rewriter {
206 Rewriter::Rewrite(config) => rewrite_model(&model, rule_sets, config),
207 }
208}
209
210/// Get a rule set by name.
211/// Returns the rule set with the given name or None if it doesn't exist.
212///
213/// # Example
214/// ```rust
215/// use conjure_cp_core::rule_engine::register_rule_set;
216/// use conjure_cp_core::rule_engine::get_rule_set_by_name;
217///
218/// register_rule_set!("MyRuleSet", ("DependencyRuleSet", "AnotherRuleSet"));
219///
220/// println!("Rule set: {:?}", get_rule_set_by_name("MyRuleSet"));
221/// ```
222///
223/// This will print:
224/// ```text
225/// Rule set: Some(RuleSet { name: "MyRuleSet", rules: OnceLock { state: Uninitialized }, dependencies: ["DependencyRuleSet", "AnotherRuleSet"] })
226/// ```
227pub fn get_rule_set_by_name(name: &str) -> Option<&'static RuleSet<'static>> {
228 get_all_rule_sets()
229 .iter()
230 .find(|rule_set| rule_set.name == name)
231 .copied()
232}
233
234/// Get all rule sets for a given solver family.
235/// Returns a `Vec` of static references to all rule sets that are applicable to the given solver family.
236/// Rule sets are not guaranteed to be in any particular order.
237///
238/// # Example
239///
240/// ```rust
241/// use conjure_cp_core::settings::SolverFamily;
242/// use conjure_cp_core::rule_engine::{get_rule_sets_for_solver_family, register_rule_set};
243///
244/// register_rule_set!("CNF", (), |f: &SolverFamily| matches!(f, SolverFamily::Sat));
245/// register_rule_set!("MinionOnly", (), |f: &SolverFamily| matches!(f, SolverFamily::Minion));
246///
247/// let rule_sets = get_rule_sets_for_solver_family(SolverFamily::Sat);
248/// assert!(rule_sets.iter().any(|rule_set| rule_set.name == "CNF"));
249/// assert!(!rule_sets.iter().any(|rule_set| rule_set.name == "MinionOnly"));
250/// ```
251pub fn get_rule_sets_for_solver_family(
252 solver_family: SolverFamily,
253) -> Vec<&'static RuleSet<'static>> {
254 get_all_rule_sets()
255 .iter()
256 .filter(|rule_set| rule_set.applies_to_family(&solver_family))
257 .copied()
258 .collect()
259}