1
//! Connect occurrence constraints to the shared cardinality and pseudo-Boolean decisions.
2
use conjure_cp::ast::{Expression as Expr, Metadata, Moo, ReturnType, SymbolTable, Typeable};
3
use conjure_cp::into_matrix_expr;
4
use conjure_cp::rule_engine::{
5
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
6
};
7

            
8
/// Matrix entries in index order, ignoring the index domain and expanding `flatten(...)`.
9
1840314
pub(super) fn matrix_entries(expression: &Expr) -> Option<Vec<Expr>> {
10
1840314
    let expression = crate::shared::utils::table_operand(expression);
11
1840314
    if let Expr::Flatten(_, None, inner) = expression {
12
2352
        fn leaves(expression: &Expr, output: &mut Vec<Expr>) -> Option<()> {
13
2352
            let expression = crate::shared::utils::table_operand(expression);
14
2352
            if let Some((entries, _)) = expression.clone().unwrap_matrix_unchecked() {
15
                for entry in entries {
16
                    leaves(&entry, output)?;
17
                }
18
2352
            } else if matches!(expression.return_type(), ReturnType::Matrix(_)) {
19
                return None;
20
            } else {
21
2352
                output.push(expression);
22
            }
23
2352
            Some(())
24
2352
        }
25
99071
        let (entries, _) = crate::shared::utils::table_operand(&inner).unwrap_matrix_unchecked()?;
26
231
        let mut output = vec![];
27
231
        for entry in entries {
28
2352
            leaves(&entry, &mut output)?;
29
        }
30
231
        Some(output)
31
    } else {
32
1741243
        expression
33
1741243
            .unwrap_matrix_unchecked()
34
            .map(|(entries, _)| entries)
35
    }
36
1840314
}
37

            
38
/// Count equality indicators, keeping the entire constraint Boolean for reified uses.
39
#[register_rule("SAT", 18500, [AtMost, AtLeast, Gcc, GccWeak])]
40
161014
fn occurrence_counts(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
41
161014
    let (vars, counts, values) = match expr {
42
        Expr::AtMost(_, vars, counts, values) | Expr::AtLeast(_, vars, counts, values) => {
43
160223
            (vars, counts, values)
44
        }
45
        Expr::Gcc(_, vars, values, counts) | Expr::GccWeak(_, vars, values, counts) => {
46
770
            (vars, counts, values)
47
        }
48
21
        _ => return Err(RuleNotApplicable),
49
    };
50
184177
    let entries = |expression: &Expr| matrix_entries(expression).ok_or(RuleNotApplicable);
51
160993
    let vars = entries(vars)?;
52
11592
    let counts = entries(counts)?;
53
11592
    let values = entries(values)?;
54
11592
    if counts.len() != values.len() {
55
        return Err(RuleNotApplicable);
56
11592
    }
57
11592
    let comparisons = values
58
11592
        .into_iter()
59
11592
        .zip(counts)
60
13923
        .map(|(value, count)| {
61
13923
            let indicators = vars
62
13923
                .iter()
63
63154
                .map(|var| {
64
63154
                    Expr::ToInt(
65
63154
                        Metadata::new(),
66
63154
                        Moo::new(Expr::Eq(
67
63154
                            Metadata::new(),
68
63154
                            Moo::new(var.clone()),
69
63154
                            Moo::new(value.clone()),
70
63154
                        )),
71
63154
                    )
72
63154
                })
73
                .collect::<Vec<_>>();
74
13923
            let total = Moo::new(Expr::Sum(
75
13923
                Metadata::new(),
76
13923
                Moo::new(into_matrix_expr!(indicators)),
77
13923
            ));
78
13923
            let count = Moo::new(count);
79
13923
            match expr {
80
6076
                Expr::AtMost(..) => Expr::Leq(Metadata::new(), total, count),
81
7700
                Expr::AtLeast(..) => Expr::Geq(Metadata::new(), total, count),
82
147
                _ => Expr::Eq(Metadata::new(), total, count),
83
            }
84
13923
        })
85
        .collect::<Vec<_>>();
86
11592
    Ok(RuleEffect::pure(Expr::And(
87
11592
        Metadata::new(),
88
11592
        Moo::new(into_matrix_expr!(comparisons)),
89
11592
    )))
90
161014
}