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 encoding_selection;
92mod resolve_rules;
93mod rewrite;
94mod rewriter_common;
95mod rule;
96mod rule_set;
97
98inventory::collect!(&'static Rule<'static>);
99
100inventory::collect!(&'static RuleSet<'static>);
101
102pub mod _dependencies {
103 pub use inventory;
104}
105
106/// Returns a copied `Vec` of all rules registered with the `register_rule` macro.
107///
108/// Rules are not guaranteed to be in any particular order.
109///
110/// # Example
111/// ```rust
112/// # use conjure_cp_core::rule_engine::{ApplicationResult, RuleEffect, get_all_rules};
113/// # use conjure_cp_core::ast::Expression;
114/// # use conjure_cp_core::ast::SymbolTable;
115/// # use conjure_cp_core::rule_engine::register_rule;
116///
117/// #[register_rule]
118/// fn identity(expr: &Expression, symbols: &SymbolTable) -> ApplicationResult {
119/// Ok(RuleEffect::pure(expr.clone()))
120/// }
121///
122/// fn main() {
123/// println!("Rules: {:?}", get_all_rules());
124/// }
125/// ```
126///
127/// This will print (if no other rules are registered):
128/// ```text
129/// Rules: [Rule { name: "identity", application: MEM }]
130/// ```
131/// Where `MEM` is the memory address of the `identity` function.
132pub fn get_all_rules() -> Vec<&'static Rule<'static>> {
133 inventory::iter::<&'static Rule<'static>>
134 .into_iter()
135 .copied()
136 .collect()
137}
138
139/// Get a rule by name.
140/// Returns the rule with the given name or None if it doesn't exist.
141///
142/// # Example
143/// ```rust
144/// use conjure_cp_core::rule_engine::register_rule;
145/// use conjure_cp_core::rule_engine::{Rule, ApplicationResult, RuleEffect, get_rule_by_name};
146/// use conjure_cp_core::ast::Expression;
147/// use conjure_cp_core::ast::SymbolTable;
148///
149/// #[register_rule]
150/// fn identity(expr: &Expression, symbols: &SymbolTable) -> ApplicationResult {
151/// Ok(RuleEffect::pure(expr.clone()))
152/// }
153///
154/// fn main() {
155/// println!("Rule: {:?}", get_rule_by_name("identity"));
156/// }
157/// ```
158///
159/// This will print:
160/// ```text
161/// Rule: Some(Rule { name: "identity", application: MEM })
162/// ```
163pub fn get_rule_by_name(name: &str) -> Option<&'static Rule<'static>> {
164 get_all_rules()
165 .iter()
166 .find(|rule| rule.name == name)
167 .copied()
168}
169
170/// Get all rule sets
171/// Returns a `Vec` of static references to all rule sets registered with the `register_rule_set` macro.
172/// Rule sets are not guaranteed to be in any particular order.
173///
174/// # Example
175/// ```rust
176/// use conjure_cp_core::rule_engine::register_rule_set;
177/// use conjure_cp_core::rule_engine::get_all_rule_sets;
178///
179/// register_rule_set!("MyRuleSet", ("AnotherRuleSet"));
180/// register_rule_set!("AnotherRuleSet", ());
181///
182/// println!("Rule sets: {:?}", get_all_rule_sets());
183/// ```
184///
185/// This will print (if no other rule sets are registered):
186/// ```text
187/// Rule sets: [
188/// RuleSet { name: "MyRuleSet", rules: OnceLock { state: Uninitialized }, dependencies: ["AnotherRuleSet"] },
189/// RuleSet { name: "AnotherRuleSet", rules: OnceLock { state: Uninitialized }, dependencies: [] }
190/// ]
191/// ```
192///
193pub fn get_all_rule_sets() -> Vec<&'static RuleSet<'static>> {
194 inventory::iter::<&'static RuleSet<'static>>
195 .into_iter()
196 .copied()
197 .collect()
198}
199
200/// Rewrites a model using the supplied rewriter configuration.
201pub fn rewrite_model_with_configured_rewriter<'a>(
202 model: Model,
203 rule_sets: &Vec<&'a RuleSet<'a>>,
204 configured_rewriter: Rewriter,
205) -> Result<Model, RewriteError> {
206 match configured_rewriter {
207 Rewriter::Rewrite(config) => rewrite_model(&model, rule_sets, config),
208 }
209}
210
211/// Get a rule set by name.
212/// Returns the rule set with the given name or None if it doesn't exist.
213///
214/// # Example
215/// ```rust
216/// use conjure_cp_core::rule_engine::register_rule_set;
217/// use conjure_cp_core::rule_engine::get_rule_set_by_name;
218///
219/// register_rule_set!("MyRuleSet", ("DependencyRuleSet", "AnotherRuleSet"));
220///
221/// println!("Rule set: {:?}", get_rule_set_by_name("MyRuleSet"));
222/// ```
223///
224/// This will print:
225/// ```text
226/// Rule set: Some(RuleSet { name: "MyRuleSet", rules: OnceLock { state: Uninitialized }, dependencies: ["DependencyRuleSet", "AnotherRuleSet"] })
227/// ```
228pub fn get_rule_set_by_name(name: &str) -> Option<&'static RuleSet<'static>> {
229 get_all_rule_sets()
230 .iter()
231 .find(|rule_set| rule_set.name == name)
232 .copied()
233}
234
235/// Get all rule sets for a given solver family.
236/// Returns a `Vec` of static references to all rule sets that are applicable to the given solver family.
237/// Rule sets are not guaranteed to be in any particular order.
238///
239/// # Example
240///
241/// ```rust
242/// use conjure_cp_core::settings::SolverFamily;
243/// use conjure_cp_core::rule_engine::{get_rule_sets_for_solver_family, register_rule_set};
244///
245/// register_rule_set!("CNF", (), |f: &SolverFamily| matches!(f, SolverFamily::Sat));
246/// register_rule_set!("MinionOnly", (), |f: &SolverFamily| matches!(f, SolverFamily::Minion));
247///
248/// let rule_sets = get_rule_sets_for_solver_family(SolverFamily::Sat);
249/// assert!(rule_sets.iter().any(|rule_set| rule_set.name == "CNF"));
250/// assert!(!rule_sets.iter().any(|rule_set| rule_set.name == "MinionOnly"));
251/// ```
252pub fn get_rule_sets_for_solver_family(
253 solver_family: SolverFamily,
254) -> Vec<&'static RuleSet<'static>> {
255 get_all_rule_sets()
256 .iter()
257 .filter(|rule_set| rule_set.applies_to_family(&solver_family))
258 .copied()
259 .collect()
260}