1
use conjure_cp::ast::Metadata;
2
use conjure_cp::ast::{Atom, Expression as Expr, Moo, SatEncodingDecision};
3
use conjure_cp::rule_engine::{
4
    ApplicationError::RuleNotApplicable, ApplicationResult, RuleEffect, register_rule,
5
};
6

            
7
use conjure_cp::ast::AbstractLiteral::Matrix;
8
use conjure_cp::ast::{Domain, SymbolTable};
9

            
10
// Atomic references can denote integers and compound values too. Those must reach their
11
// representation's comparison rules rather than becoming Boolean gate inputs.
12
40450453
fn is_literal(expr: &Expr) -> bool {
13
40450453
    crate::shared::utils::is_literal(expr)
14
30768978
        && expr.domain_of().is_some_and(|domain| domain.is_bool())
15
40450453
}
16

            
17
143446200
pub(super) fn create_bool_aux(symbols: &mut SymbolTable) -> Expr {
18
143446200
    let name = symbols.gen_find_auxiliary(&Domain::bool());
19

            
20
143446200
    symbols.insert(name.clone());
21

            
22
143446200
    Expr::Atomic(
23
143446200
        Metadata::new(),
24
143446200
        Atom::Reference(conjure_cp::ast::Reference::new(name)),
25
143446200
    )
26
143446200
}
27

            
28
/// Boolean order is false before true and shares the existing gate decisions.
29
#[register_rule("SAT", 18500, [Lt, Gt, Leq, Geq])]
30
6614057
fn boolean_order(expr: &Expr, _: &SymbolTable) -> ApplicationResult {
31
6614057
    let (left, right, strict) = match expr {
32
2116333
        Expr::Lt(_, left, right) => (left, right, true),
33
230888
        Expr::Gt(_, left, right) => (right, left, true),
34
3357812
        Expr::Leq(_, left, right) => (left, right, false),
35
909003
        Expr::Geq(_, left, right) => (right, left, false),
36
21
        _ => return Err(RuleNotApplicable),
37
    };
38
6614036
    if [left, right]
39
6614036
        .iter()
40
6624613
        .any(|operand| !operand.domain_of().is_some_and(|domain| domain.is_bool()))
41
    {
42
6603459
        return Err(RuleNotApplicable);
43
10577
    }
44
10577
    Ok(RuleEffect::pure(if strict {
45
10577
        Expr::And(
46
10577
            Metadata::new(),
47
10577
            Moo::new(conjure_cp::into_matrix_expr!(vec![
48
10577
                Expr::Not(Metadata::new(), left.clone()),
49
10577
                (**right).clone(),
50
10577
            ])),
51
10577
        )
52
    } else {
53
        Expr::Imply(Metadata::new(), left.clone(), right.clone())
54
    }))
55
6614057
}
56

            
57
/// Record an AND gate without generating clauses.
58
52345727
pub fn tseytin_and(
59
52345727
    exprs: &[Expr],
60
52345727
    decisions: &mut Vec<SatEncodingDecision>,
61
52345727
    symbols: &mut SymbolTable,
62
52345727
) -> Expr {
63
52345727
    gate(
64
52345727
        Expr::And(
65
52345727
            Metadata::new(),
66
52345727
            Moo::new(conjure_cp::into_matrix_expr!(exprs.to_vec())),
67
52345727
        ),
68
52345727
        decisions,
69
52345727
        symbols,
70
52345727
    )
71
52345727
}
72

            
73
/// Record a NOT gate without generating clauses.
74
8897046
pub fn tseytin_not(
75
8897046
    x: Expr,
76
8897046
    decisions: &mut Vec<SatEncodingDecision>,
77
8897046
    symbols: &mut SymbolTable,
78
8897046
) -> Expr {
79
8897046
    gate(Expr::Not(Metadata::new(), Moo::new(x)), decisions, symbols)
80
8897046
}
81

            
82
/// Record an OR gate without generating clauses.
83
23156759
pub fn tseytin_or(
84
23156759
    exprs: &[Expr],
85
23156759
    decisions: &mut Vec<SatEncodingDecision>,
86
23156759
    symbols: &mut SymbolTable,
87
23156759
) -> Expr {
88
23156759
    gate(
89
23156759
        Expr::Or(
90
23156759
            Metadata::new(),
91
23156759
            Moo::new(conjure_cp::into_matrix_expr!(exprs.to_vec())),
92
23156759
        ),
93
23156759
        decisions,
94
23156759
        symbols,
95
23156759
    )
96
23156759
}
97

            
98
/// Record a Boolean equivalence gate.
99
28887
pub fn tseytin_iff(
100
28887
    x: Expr,
101
28887
    y: Expr,
102
28887
    decisions: &mut Vec<SatEncodingDecision>,
103
28887
    symbols: &mut SymbolTable,
104
28887
) -> Expr {
105
28887
    gate(
106
28887
        Expr::Iff(Metadata::new(), Moo::new(x), Moo::new(y)),
107
28887
        decisions,
108
28887
        symbols,
109
28887
    )
110
28887
}
111

            
112
/// Record a Boolean implication gate.
113
1966
pub fn tseytin_imply(
114
1966
    x: Expr,
115
1966
    y: Expr,
116
1966
    decisions: &mut Vec<SatEncodingDecision>,
117
1966
    symbols: &mut SymbolTable,
118
1966
) -> Expr {
119
1966
    gate(
120
1966
        Expr::Imply(Metadata::new(), Moo::new(x), Moo::new(y)),
121
1966
        decisions,
122
1966
        symbols,
123
1966
    )
124
1966
}
125

            
126
/// Record a conditional Boolean choice.
127
#[allow(dead_code)]
128
16395265
pub fn tseytin_mux(
129
16395265
    cond: Expr,
130
16395265
    a: Expr,
131
16395265
    b: Expr,
132
16395265
    decisions: &mut Vec<SatEncodingDecision>,
133
16395265
    symbols: &mut SymbolTable,
134
16395265
) -> Expr {
135
16395265
    let when_true = Expr::And(
136
16395265
        Metadata::new(),
137
16395265
        Moo::new(conjure_cp::into_matrix_expr!(vec![cond.clone(), b])),
138
16395265
    );
139
16395265
    let when_false = Expr::And(
140
16395265
        Metadata::new(),
141
16395265
        Moo::new(conjure_cp::into_matrix_expr!(vec![
142
16395265
            Expr::Not(Metadata::new(), Moo::new(cond)),
143
16395265
            a
144
16395265
        ])),
145
16395265
    );
146
16395265
    gate(
147
16395265
        Expr::Or(
148
16395265
            Metadata::new(),
149
16395265
            Moo::new(conjure_cp::into_matrix_expr!(vec![when_true, when_false])),
150
16395265
        ),
151
16395265
        decisions,
152
16395265
        symbols,
153
16395265
    )
154
16395265
}
155

            
156
/// Record a Boolean exclusive-or gate.
157
#[allow(dead_code)]
158
40265275
pub fn tseytin_xor(
159
40265275
    x: Expr,
160
40265275
    y: Expr,
161
40265275
    decisions: &mut Vec<SatEncodingDecision>,
162
40265275
    symbols: &mut SymbolTable,
163
40265275
) -> Expr {
164
40265275
    gate(
165
40265275
        Expr::Not(
166
40265275
            Metadata::new(),
167
40265275
            Moo::new(Expr::Iff(Metadata::new(), Moo::new(x), Moo::new(y))),
168
40265275
        ),
169
40265275
        decisions,
170
40265275
        symbols,
171
40265275
    )
172
40265275
}
173

            
174
141090925
fn gate(
175
141090925
    expression: Expr,
176
141090925
    decisions: &mut Vec<SatEncodingDecision>,
177
141090925
    symbols: &mut SymbolTable,
178
141090925
) -> Expr {
179
141090925
    let output = create_bool_aux(symbols);
180
141090925
    decisions.push(SatEncodingDecision::Boolean {
181
141090925
        output: output.clone(),
182
141090925
        expression,
183
141090925
    });
184
141090925
    output
185
141090925
}
186

            
187
/// Preserve ready assertions before the ordinary equivalence-gate rules consume them.
188
#[register_rule("SAT", 18000, [Root])]
189
6229444
fn select_asserted_boolean(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
190
42809627
    super::asserted::select_asserted(expr, symbols, |child| {
191
42809627
        (child != &true.into() && ready_boolean(child))
192
2464532
            .then(|| SatEncodingDecision::Assert(child.clone()))
193
42809627
    })
194
6229444
}
195

            
196
63512309
fn ready_boolean(expr: &Expr) -> bool {
197
63512309
    match expr {
198
16652797
        Expr::Atomic(..) => is_literal(expr),
199
1417729
        Expr::Not(_, input) => ready_boolean(input),
200
        Expr::And(_, inputs) | Expr::Or(_, inputs) => {
201
14510689
            matches!(inputs.as_ref(), Expr::AbstractLiteral(_, Matrix(entries, _))
202
13489263
                if entries.iter().all(ready_boolean))
203
        }
204
        Expr::Imply(_, left, right)
205
        | Expr::Iff(_, left, right)
206
        | Expr::Eq(_, left, right)
207
12532934
        | Expr::Neq(_, left, right) => ready_boolean(left) && ready_boolean(right),
208
18398160
        _ => false,
209
    }
210
63512309
}
211

            
212
/// Lower the Boolean operation to a semantic SAT gate decision.
213
#[register_rule("SAT", 8500, [And, Or])]
214
3377103
fn apply_tseytin_and_or(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
215
3377103
    let exprs = match expr {
216
3377082
        Expr::And(_, exprs) | Expr::Or(_, exprs) => exprs,
217
21
        _ => return Err(RuleNotApplicable),
218
    };
219

            
220
3377082
    let Expr::AbstractLiteral(_, Matrix(exprs_list, _)) = exprs.as_ref() else {
221
145280
        return Err(RuleNotApplicable);
222
    };
223

            
224
3231802
    for x in exprs_list {
225
5521820
        if !is_literal(x) {
226
3063692
            return Err(RuleNotApplicable);
227
2458128
        };
228
    }
229

            
230
    let new_expr;
231
168110
    let mut new_sat_decisions = vec![];
232
168110
    let mut new_symbols = symbols.clone();
233

            
234
168110
    match expr {
235
        Expr::And(_, _) => {
236
123548
            new_expr = tseytin_and(exprs_list, &mut new_sat_decisions, &mut new_symbols);
237
        }
238
        Expr::Or(_, _) => {
239
44562
            new_expr = tseytin_or(exprs_list, &mut new_sat_decisions, &mut new_symbols);
240
        }
241
        _ => return Err(RuleNotApplicable),
242
    };
243

            
244
168110
    Ok(RuleEffect::sat(new_expr, new_sat_decisions, new_symbols))
245
3377103
}
246

            
247
/// Lower the Boolean operation to a semantic SAT gate decision.
248
#[register_rule("SAT", 9005, [Not])]
249
102214
fn apply_tseytin_not(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
250
102214
    let Expr::Not(_, x) = expr else {
251
21
        return Err(RuleNotApplicable);
252
    };
253

            
254
102193
    let Expr::Atomic(_, _) = x.as_ref() else {
255
85939
        return Err(RuleNotApplicable);
256
    };
257

            
258
16254
    if !is_literal(x.as_ref()) {
259
        return Err(RuleNotApplicable);
260
16254
    };
261

            
262
16254
    let mut new_sat_decisions = vec![];
263
16254
    let mut new_symbols = symbols.clone();
264

            
265
16254
    let new_expr = tseytin_not(x.as_ref().clone(), &mut new_sat_decisions, &mut new_symbols);
266

            
267
16254
    Ok(RuleEffect::sat(new_expr, new_sat_decisions, new_symbols))
268
102214
}
269

            
270
/// Lower the Boolean operation to a semantic SAT gate decision.
271
#[register_rule("SAT", 8500, [Iff, Eq])]
272
2814541
fn apply_tseytin_iff_eq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
273
    // Check for iff or eq
274
2814541
    let (x, y) = match expr {
275
2814520
        Expr::Iff(_, x, y) | Expr::Eq(_, x, y) => (x, y),
276
21
        _ => return Err(RuleNotApplicable),
277
    };
278

            
279
2814520
    if !is_literal(x.as_ref()) || !is_literal(y.as_ref()) {
280
2814053
        return Err(RuleNotApplicable);
281
467
    };
282

            
283
467
    let mut new_sat_decisions = vec![];
284
467
    let mut new_symbols = symbols.clone();
285

            
286
467
    let new_expr = tseytin_iff(
287
467
        x.as_ref().clone(),
288
467
        y.as_ref().clone(),
289
467
        &mut new_sat_decisions,
290
467
        &mut new_symbols,
291
467
    );
292

            
293
467
    Ok(RuleEffect::sat(new_expr, new_sat_decisions, new_symbols))
294
2814541
}
295

            
296
/// Lower the Boolean operation to a semantic SAT gate decision.
297
#[register_rule("SAT", 8500, [Imply])]
298
62852
fn apply_tseytin_imply(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
299
62852
    let Expr::Imply(_, x, y) = expr else {
300
21
        return Err(RuleNotApplicable);
301
    };
302

            
303
62831
    if !is_literal(x.as_ref()) || !is_literal(y.as_ref()) {
304
60865
        return Err(RuleNotApplicable);
305
1966
    };
306

            
307
    let new_expr;
308
1966
    let mut new_sat_decisions = vec![];
309
1966
    let mut new_symbols = symbols.clone();
310

            
311
1966
    new_expr = tseytin_imply(
312
1966
        x.as_ref().clone(),
313
1966
        y.as_ref().clone(),
314
1966
        &mut new_sat_decisions,
315
1966
        &mut new_symbols,
316
1966
    );
317

            
318
1966
    Ok(RuleEffect::sat(new_expr, new_sat_decisions, new_symbols))
319
62852
}
320

            
321
/// Lower the Boolean operation to a semantic SAT gate decision.
322
#[register_rule("SAT", 8500, [Neq])]
323
98490
fn apply_tseytin_xor_neq(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
324
98490
    let Expr::Neq(_, x, y) = expr else {
325
21
        return Err(RuleNotApplicable);
326
    };
327

            
328
98469
    if !is_literal(x.as_ref()) || !is_literal(y.as_ref()) {
329
93079
        return Err(RuleNotApplicable);
330
5390
    };
331

            
332
5390
    let mut new_sat_decisions = vec![];
333
5390
    let mut new_symbols = symbols.clone();
334

            
335
5390
    let new_expr = tseytin_xor(
336
5390
        x.as_ref().clone(),
337
5390
        y.as_ref().clone(),
338
5390
        &mut new_sat_decisions,
339
5390
        &mut new_symbols,
340
5390
    );
341

            
342
5390
    Ok(RuleEffect::sat(new_expr, new_sat_decisions, new_symbols))
343
98490
}
344

            
345
/// Shared recognition keeps asserted Boolean counts in their own encoding family.
346
12235175
pub(super) fn count_inputs(sum: &Expr) -> Option<Vec<Expr>> {
347
12235175
    let Expr::AbstractLiteral(_, Matrix(terms, _)) = sum else {
348
483833
        return None;
349
    };
350
11751342
    terms
351
11751342
        .iter()
352
21811042
        .map(|term| match term {
353
            Expr::ToInt(_, input)
354
15101856
                if is_literal(input)
355
11439288
                    && input.domain_of().is_some_and(|domain| domain.is_bool()) =>
356
            {
357
11439288
                Some(input.as_ref().clone())
358
            }
359
            Expr::Atomic(_, Atom::Literal(conjure_cp::ast::Literal::Int(value)))
360
703669
                if *value == 0 || *value == 1 =>
361
            {
362
281337
                Some((*value == 1).into())
363
            }
364
10090417
            _ => None,
365
21811042
        })
366
        .collect::<Option<Vec<_>>>()
367
12235175
}
368

            
369
/// Preserve asserted Boolean counts until a library encoder is selected.
370
#[register_rule("SAT", 20000, [Root])]
371
7779833
fn select_cardinality(expr: &Expr, symbols: &SymbolTable) -> ApplicationResult {
372
    use conjure_cp::ast::sat_decision::CardinalityRelation;
373
76655931
    super::asserted::select_asserted(expr, symbols, |child| {
374
76655931
        let (left, right, mut relation) = match child {
375
3088973
            Expr::Leq(_, left, right) => (left, right, CardinalityRelation::AtMost),
376
895639
            Expr::Geq(_, left, right) => (left, right, CardinalityRelation::AtLeast),
377
6263795
            Expr::Eq(_, left, right) => (left, right, CardinalityRelation::Exactly),
378
66407524
            _ => return None,
379
        };
380
10248407
        let (sum, bound) = match (left.as_ref(), right.as_ref()) {
381
            (
382
                Expr::Sum(_, terms),
383
                Expr::Atomic(_, Atom::Literal(conjure_cp::ast::Literal::Int(bound))),
384
4407279
            ) => (terms, *bound),
385
            (
386
                Expr::Atomic(_, Atom::Literal(conjure_cp::ast::Literal::Int(bound))),
387
                Expr::Sum(_, terms),
388
            ) => {
389
5201
                relation = match relation {
390
                    CardinalityRelation::AtMost => CardinalityRelation::AtLeast,
391
                    CardinalityRelation::AtLeast => CardinalityRelation::AtMost,
392
5201
                    other => other,
393
                };
394
5201
                (terms, *bound)
395
            }
396
5835927
            _ => return None,
397
        };
398
4412480
        let inputs = count_inputs(sum)?;
399
645799
        Some(if relation == CardinalityRelation::AtMost && bound == 1 {
400
581252
            SatEncodingDecision::AtMostOne {
401
581252
                inputs,
402
581252
                encoding: None,
403
581252
            }
404
        } else {
405
64547
            SatEncodingDecision::Cardinality {
406
64547
                inputs,
407
64547
                relation,
408
64547
                bound: i64::from(bound),
409
64547
                encoding: None,
410
64547
            }
411
        })
412
76655931
    })
413
7779833
}