Skip to main content

conjure_cp_core/rule_engine/
mod.rs

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