1
use conjure_core::ast::{
2
    DecisionVariable, Expression as Expr, Factor, Literal as Lit, SymbolTable,
3
};
4
use conjure_core::metadata::Metadata;
5
use conjure_core::rule_engine::{
6
    register_rule, register_rule_set, ApplicationError, ApplicationError::RuleNotApplicable,
7
    ApplicationResult, Reduction,
8
};
9
use conjure_core::Model;
10
use uniplate::Uniplate;
11

            
12
use Expr::*;
13
use Factor::*;
14
use Lit::*;
15

            
16
/*****************************************************************************/
17
/*        This file contains basic rules for simplifying expressions         */
18
/*****************************************************************************/
19

            
20
register_rule_set!("Base", 100, ());
21

            
22
/// This rule simplifies expressions where the operator is applied to an empty set of sub-expressions.
23
///
24
/// For example:
25
/// - `or([])` simplifies to `false` since no disjunction exists.
26
///
27
/// **Applicable examples:**
28
/// ```text
29
/// or([])  ~> false
30
/// X([]) ~> Nothing
31
/// ```
32
#[register_rule(("Base", 8800))]
33
8217307
fn remove_empty_expression(expr: &Expr, _: &Model) -> ApplicationResult {
34
    // excluded expressions
35
2082840
    if matches!(
36
6133787
        expr,
37
        FactorE(_, Reference(_,)) | FactorE(_, Literal(_)) | WatchedLiteral(_, _, _)
38
    ) {
39
6134467
        return Err(ApplicationError::RuleNotApplicable);
40
2082840
    }
41
2082840

            
42
2082840
    if !expr.children().is_empty() {
43
2082789
        return Err(ApplicationError::RuleNotApplicable);
44
51
    }
45

            
46
51
    let new_expr = match expr {
47
17
        Or(_, _) => FactorE(Metadata::new(), Literal(Bool(false))),
48
34
        _ => And(Metadata::new(), vec![]), // TODO: (yb33) Change it to a simple vector after we refactor our model,
49
    };
50

            
51
51
    Ok(Reduction::pure(new_expr))
52
8217307
}
53

            
54
/**
55
 * Unwrap trivial sums:
56
 * ```text
57
1
 * sum([a]) = a
58
1
 * ```
59
1
 */
60
#[register_rule(("Base", 8800))]
61
8217341
fn unwrap_sum(expr: &Expr, _: &Model) -> ApplicationResult {
62
204
    match expr {
63
204
        Sum(_, exprs) if (exprs.len() == 1) => Ok(Reduction::pure(exprs[0].clone())),
64
8217307
        _ => Err(ApplicationError::RuleNotApplicable),
65
    }
66
8217341
}
67

            
68
/**
69
 * Flatten nested sums:
70
 * ```text
71
1
 * sum(sum(a, b), c) = sum(a, b, c)
72
1
 * ```
73
1
 */
74
#[register_rule(("Base", 8800))]
75
8217307
pub fn flatten_nested_sum(expr: &Expr, _: &Model) -> ApplicationResult {
76
8217307
    match expr {
77
170
        Sum(metadata, exprs) => {
78
170
            let mut new_exprs = Vec::new();
79
170
            let mut changed = false;
80
646
            for e in exprs {
81
476
                match e {
82
170
                    Sum(_, sub_exprs) => {
83
170
                        changed = true;
84
510
                        for e in sub_exprs {
85
340
                            new_exprs.push(e.clone());
86
340
                        }
87
                    }
88
306
                    _ => new_exprs.push(e.clone()),
89
                }
90
            }
91
170
            if !changed {
92
                return Err(ApplicationError::RuleNotApplicable);
93
170
            }
94
170
            Ok(Reduction::pure(Sum(metadata.clone_dirty(), new_exprs)))
95
        }
96
8217137
        _ => Err(ApplicationError::RuleNotApplicable),
97
    }
98
8217307
}
99

            
100
/**
101
* Unwrap nested `or`
102

            
103
* ```text
104
1
* or(or(a, b), c) = or(a, b, c)
105
1
* ```
106
1
 */
107
#[register_rule(("Base", 8800))]
108
8217341
fn unwrap_nested_or(expr: &Expr, _: &Model) -> ApplicationResult {
109
8217341
    match expr {
110
428332
        Or(metadata, exprs) => {
111
428332
            let mut new_exprs = Vec::new();
112
428332
            let mut changed = false;
113
2093074
            for e in exprs {
114
1664742
                match e {
115
85
                    Or(_, exprs) => {
116
85
                        changed = true;
117
255
                        for e in exprs {
118
170
                            new_exprs.push(e.clone());
119
170
                        }
120
                    }
121
1664657
                    _ => new_exprs.push(e.clone()),
122
                }
123
            }
124
428332
            if !changed {
125
428247
                return Err(ApplicationError::RuleNotApplicable);
126
85
            }
127
85
            Ok(Reduction::pure(Or(metadata.clone_dirty(), new_exprs)))
128
        }
129
7789009
        _ => Err(ApplicationError::RuleNotApplicable),
130
    }
131
8217341
}
132

            
133
/**
134
* Unwrap nested `and`
135

            
136
* ```text
137
1
* and(and(a, b), c) = and(a, b, c)
138
1
* ```
139
1
 */
140
#[register_rule(("Base", 8800))]
141
8217341
fn unwrap_nested_and(expr: &Expr, _: &Model) -> ApplicationResult {
142
8217341
    match expr {
143
19023
        And(metadata, exprs) => {
144
19023
            let mut new_exprs = Vec::new();
145
19023
            let mut changed = false;
146
613972
            for e in exprs {
147
594949
                match e {
148
4216
                    And(_, exprs) => {
149
4216
                        changed = true;
150
12716
                        for e in exprs {
151
8500
                            new_exprs.push(e.clone());
152
8500
                        }
153
                    }
154
590733
                    _ => new_exprs.push(e.clone()),
155
                }
156
            }
157
19023
            if !changed {
158
14807
                return Err(ApplicationError::RuleNotApplicable);
159
4216
            }
160
4216
            Ok(Reduction::pure(And(metadata.clone_dirty(), new_exprs)))
161
        }
162
8198318
        _ => Err(ApplicationError::RuleNotApplicable),
163
    }
164
8217341
}
165

            
166
/**
167
* Remove double negation:
168

            
169
* ```text
170
1
* not(not(a)) = a
171
1
* ```
172
1
 */
173
#[register_rule(("Base", 8400))]
174
8217324
fn remove_double_negation(expr: &Expr, _: &Model) -> ApplicationResult {
175
8217324
    match expr {
176
85
        Not(_, contents) => match contents.as_ref() {
177
17
            Not(_, expr_box) => Ok(Reduction::pure(*expr_box.clone())),
178
68
            _ => Err(ApplicationError::RuleNotApplicable),
179
        },
180
8217239
        _ => Err(ApplicationError::RuleNotApplicable),
181
    }
182
8217324
}
183

            
184
/**
185
 * Remove trivial `and` (only one element):
186
 * ```text
187
1
 * and([a]) = a
188
1
 * ```
189
1
 */
190
#[register_rule(("Base", 8800))]
191
8217324
fn remove_trivial_and(expr: &Expr, _: &Model) -> ApplicationResult {
192
8217324
    match expr {
193
19006
        And(_, exprs) => {
194
19006
            if exprs.len() == 1 {
195
17
                return Ok(Reduction::pure(exprs[0].clone()));
196
18989
            }
197
18989
            Err(ApplicationError::RuleNotApplicable)
198
        }
199
8198318
        _ => Err(ApplicationError::RuleNotApplicable),
200
    }
201
8217324
}
202

            
203
/**
204
 * Remove trivial `or` (only one element):
205
 * ```text
206
1
 * or([a]) = a
207
1
 * ```
208
1
 */
209
#[register_rule(("Base", 8800))]
210
8217324
fn remove_trivial_or(expr: &Expr, _: &Model) -> ApplicationResult {
211
428315
    match expr {
212
428315
        // do not conflict with unwrap_nested_or rule.
213
428315
        Or(_, exprs) if exprs.len() == 1 && !matches!(exprs[0], Or(_, _)) => {
214
51
            Ok(Reduction::pure(exprs[0].clone()))
215
        }
216
8217273
        _ => Err(ApplicationError::RuleNotApplicable),
217
    }
218
8217324
}
219

            
220
/**
221
<<<<<<< HEAD
222
 * Remove constant bools from or expressions
223
 * ```text
224
 * or([true, a]) = true
225
1
 * or([false, a]) = a
226
1
 * ```
227
1
 */
228
#[register_rule(("Base", 100))]
229
8217307
fn remove_constants_from_or(expr: &Expr, _: &Model) -> ApplicationResult {
230
8217307
    match expr {
231
428298
        Or(metadata, exprs) => {
232
428298
            let mut new_exprs = Vec::new();
233
428298
            let mut changed = false;
234
2092938
            for e in exprs {
235
68
                match e {
236
68
                    FactorE(metadata, Literal(Bool(val))) => {
237
68
                        if *val {
238
                            // If we find a true, the whole expression is true
239
34
                            return Ok(Reduction::pure(FactorE(
240
34
                                metadata.clone_dirty(),
241
34
                                Literal(Bool(true)),
242
34
                            )));
243
34
                        } else {
244
34
                            // If we find a false, we can ignore it
245
34
                            changed = true;
246
34
                        }
247
                    }
248
1664606
                    _ => new_exprs.push(e.clone()),
249
                }
250
            }
251
428264
            if !changed {
252
428230
                return Err(ApplicationError::RuleNotApplicable);
253
34
            }
254
34
            Ok(Reduction::pure(Or(metadata.clone_dirty(), new_exprs)))
255
        }
256
7789009
        _ => Err(ApplicationError::RuleNotApplicable),
257
    }
258
8217307
}
259

            
260
/**
261
 * Remove constant bools from and expressions
262
 * ```text
263
 * and([true, a]) = a
264
1
 * and([false, a]) = false
265
1
 * ```
266
1
 */
267
#[register_rule(("Base", 100))]
268
8217307
fn remove_constants_from_and(expr: &Expr, _: &Model) -> ApplicationResult {
269
8217307
    match expr {
270
18989
        And(metadata, exprs) => {
271
18989
            let mut new_exprs = Vec::new();
272
18989
            let mut changed = false;
273
613870
            for e in exprs {
274
68
                match e {
275
68
                    FactorE(metadata, Literal(Bool(val))) => {
276
68
                        if !*val {
277
                            // If we find a false, the whole expression is false
278
                            return Ok(Reduction::pure(FactorE(
279
                                metadata.clone_dirty(),
280
                                Literal(Bool(false)),
281
                            )));
282
68
                        } else {
283
68
                            // If we find a true, we can ignore it
284
68
                            changed = true;
285
68
                        }
286
                    }
287
594813
                    _ => new_exprs.push(e.clone()),
288
                }
289
            }
290
18989
            if !changed {
291
18921
                return Err(ApplicationError::RuleNotApplicable);
292
68
            }
293
68
            Ok(Reduction::pure(And(metadata.clone_dirty(), new_exprs)))
294
        }
295
8198318
        _ => Err(ApplicationError::RuleNotApplicable),
296
    }
297
8217307
}
298

            
299
/**
300
 * Evaluate Not expressions with constant bools
301
 * ```text
302
 * not(true) = false
303
1
 * not(false) = true
304
1
 * ```
305
1
 */
306
#[register_rule(("Base", 100))]
307
8217307
fn evaluate_constant_not(expr: &Expr, _: &Model) -> ApplicationResult {
308
8217307
    match expr {
309
68
        Not(_, contents) => match contents.as_ref() {
310
            FactorE(metadata, Literal(Bool(val))) => Ok(Reduction::pure(FactorE(
311
                metadata.clone_dirty(),
312
                Literal(Bool(!val)),
313
            ))),
314
68
            _ => Err(ApplicationError::RuleNotApplicable),
315
        },
316
8217239
        _ => Err(ApplicationError::RuleNotApplicable),
317
    }
318
8217307
}
319

            
320
/**
321
 * Turn a Min into a new variable and post a top-level constraint to ensure the new variable is the minimum.
322
 * ```text
323
1
 * min([a, b]) ~> c ; c <= a & c <= b & (c = a | c = b)
324
1
 * ```
325
1
 */
326
#[register_rule(("Base", 2000))]
327
8217307
fn min_to_var(expr: &Expr, mdl: &Model) -> ApplicationResult {
328
8217307
    match expr {
329
119
        Min(metadata, exprs) => {
330
119
            let new_name = mdl.gensym();
331
119

            
332
119
            let mut new_top = Vec::new(); // the new variable must be less than or equal to all the other variables
333
119
            let mut disjunction = Vec::new(); // the new variable must be equal to one of the variables
334
357
            for e in exprs {
335
238
                new_top.push(Leq(
336
238
                    Metadata::new(),
337
238
                    Box::new(FactorE(Metadata::new(), Reference(new_name.clone()))),
338
238
                    Box::new(e.clone()),
339
238
                ));
340
238
                disjunction.push(Eq(
341
238
                    Metadata::new(),
342
238
                    Box::new(FactorE(Metadata::new(), Reference(new_name.clone()))),
343
238
                    Box::new(e.clone()),
344
238
                ));
345
238
            }
346
119
            new_top.push(Or(Metadata::new(), disjunction));
347
119

            
348
119
            let mut new_vars = SymbolTable::new();
349
119
            let domain = expr
350
119
                .domain_of(&mdl.variables)
351
119
                .ok_or(ApplicationError::DomainError)?;
352
119
            new_vars.insert(new_name.clone(), DecisionVariable::new(domain));
353
119

            
354
119
            Ok(Reduction::new(
355
119
                FactorE(Metadata::new(), Reference(new_name)),
356
119
                And(metadata.clone_dirty(), new_top),
357
119
                new_vars,
358
119
            ))
359
        }
360
8217188
        _ => Err(ApplicationError::RuleNotApplicable),
361
    }
362
8217307
}
363

            
364
/**
365
 * Turn a Max into a new variable and post a top level constraint to ensure the new variable is the maximum.
366
 * ```text
367
1
 * max([a, b]) ~> c ; c >= a & c >= b & (c = a | c = b)
368
1
 * ```
369
1
 */
370
#[register_rule(("Base", 100))]
371
8217307
fn max_to_var(expr: &Expr, mdl: &Model) -> ApplicationResult {
372
8217307
    match expr {
373
68
        Max(metadata, exprs) => {
374
68
            let new_name = mdl.gensym();
375
68

            
376
68
            let mut new_top = Vec::new(); // the new variable must be more than or equal to all the other variables
377
68
            let mut disjunction = Vec::new(); // the new variable must more than or equal to one of the variables
378
204
            for e in exprs {
379
136
                new_top.push(Geq(
380
136
                    Metadata::new(),
381
136
                    Box::new(FactorE(Metadata::new(), Reference(new_name.clone()))),
382
136
                    Box::new(e.clone()),
383
136
                ));
384
136
                disjunction.push(Eq(
385
136
                    Metadata::new(),
386
136
                    Box::new(FactorE(Metadata::new(), Reference(new_name.clone()))),
387
136
                    Box::new(e.clone()),
388
136
                ));
389
136
            }
390
68
            new_top.push(Or(Metadata::new(), disjunction));
391
68

            
392
68
            let mut new_vars = SymbolTable::new();
393
68
            let domain = expr
394
68
                .domain_of(&mdl.variables)
395
68
                .ok_or(ApplicationError::DomainError)?;
396
68
            new_vars.insert(new_name.clone(), DecisionVariable::new(domain));
397
68

            
398
68
            Ok(Reduction::new(
399
68
                FactorE(Metadata::new(), Reference(new_name)),
400
68
                And(metadata.clone_dirty(), new_top),
401
68
                new_vars,
402
68
            ))
403
        }
404
8217239
        _ => Err(ApplicationError::RuleNotApplicable),
405
    }
406
8217307
}
407

            
408
/**
409
* Apply the Distributive Law to expressions like `Or([..., And(a, b)])`
410

            
411
* ```text
412
1
* or(and(a, b), c) = and(or(a, c), or(b, c))
413
1
* ```
414
1
 */
415
#[register_rule(("Base", 8400))]
416
8217324
fn distribute_or_over_and(expr: &Expr, _: &Model) -> ApplicationResult {
417
428315
    fn find_and(exprs: &[Expr]) -> Option<usize> {
418
        // ToDo: may be better to move this to some kind of utils module?
419
1656259
        for (i, e) in exprs.iter().enumerate() {
420
1656259
            if let And(_, _) = e {
421
3944
                return Some(i);
422
1652315
            }
423
        }
424
424371
        None
425
428315
    }
426

            
427
8217324
    match expr {
428
428315
        Or(_, exprs) => match find_and(exprs) {
429
3944
            Some(idx) => {
430
3944
                let mut rest = exprs.clone();
431
3944
                let and_expr = rest.remove(idx);
432
3944

            
433
3944
                match and_expr {
434
3944
                    And(metadata, and_exprs) => {
435
3944
                        let mut new_and_contents = Vec::new();
436

            
437
11832
                        for e in and_exprs {
438
                            // ToDo: Cloning everything may be a bit inefficient - discuss
439
7888
                            let mut new_or_contents = rest.clone();
440
7888
                            new_or_contents.push(e.clone());
441
7888
                            new_and_contents.push(Or(metadata.clone_dirty(), new_or_contents))
442
                        }
443

            
444
3944
                        Ok(Reduction::pure(And(
445
3944
                            metadata.clone_dirty(),
446
3944
                            new_and_contents,
447
3944
                        )))
448
                    }
449
                    _ => Err(ApplicationError::RuleNotApplicable),
450
                }
451
            }
452
424371
            None => Err(ApplicationError::RuleNotApplicable),
453
        },
454
7789009
        _ => Err(ApplicationError::RuleNotApplicable),
455
    }
456
8217324
}
457

            
458
/**
459
* Distribute `not` over `and` (De Morgan's Law):
460

            
461
* ```text
462
1
* not(and(a, b)) = or(not a, not b)
463
1
* ```
464
1
 */
465
#[register_rule(("Base", 8400))]
466
8217341
fn distribute_not_over_and(expr: &Expr, _: &Model) -> ApplicationResult {
467
8217341
    match expr {
468
102
        Not(_, contents) => match contents.as_ref() {
469
17
            And(metadata, exprs) => {
470
17
                if exprs.len() == 1 {
471
                    let single_expr = exprs[0].clone();
472
                    return Ok(Reduction::pure(Not(
473
                        Metadata::new(),
474
                        Box::new(single_expr.clone()),
475
                    )));
476
17
                }
477
17
                let mut new_exprs = Vec::new();
478
51
                for e in exprs {
479
34
                    new_exprs.push(Not(metadata.clone(), Box::new(e.clone())));
480
34
                }
481
17
                Ok(Reduction::pure(Or(metadata.clone(), new_exprs)))
482
            }
483
85
            _ => Err(ApplicationError::RuleNotApplicable),
484
        },
485
8217239
        _ => Err(ApplicationError::RuleNotApplicable),
486
    }
487
8217341
}
488

            
489
/**
490
* Distribute `not` over `or` (De Morgan's Law):
491

            
492
* ```text
493
1
* not(or(a, b)) = and(not a, not b)
494
1
* ```
495
1
 */
496
#[register_rule(("Base", 8400))]
497
8217341
fn distribute_not_over_or(expr: &Expr, _: &Model) -> ApplicationResult {
498
8217341
    match expr {
499
102
        Not(_, contents) => match contents.as_ref() {
500
17
            Or(metadata, exprs) => {
501
17
                if exprs.len() == 1 {
502
                    let single_expr = exprs[0].clone();
503
                    return Ok(Reduction::pure(Not(
504
                        Metadata::new(),
505
                        Box::new(single_expr.clone()),
506
                    )));
507
17
                }
508
17
                let mut new_exprs = Vec::new();
509
51
                for e in exprs {
510
34
                    new_exprs.push(Not(metadata.clone(), Box::new(e.clone())));
511
34
                }
512
17
                Ok(Reduction::pure(And(metadata.clone(), new_exprs)))
513
            }
514
85
            _ => Err(ApplicationError::RuleNotApplicable),
515
        },
516
8217239
        _ => Err(ApplicationError::RuleNotApplicable),
517
    }
518
8217341
}
519

            
520
#[register_rule(("Base", 8800))]
521
8217307
fn negated_neq_to_eq(expr: &Expr, _: &Model) -> ApplicationResult {
522
8217307
    match expr {
523
68
        Not(_, a) => match a.as_ref() {
524
            Neq(_, b, c) if (!b.can_be_undefined() && !c.can_be_undefined()) => {
525
                Ok(Reduction::pure(Eq(Metadata::new(), b.clone(), c.clone())))
526
            }
527
68
            _ => Err(RuleNotApplicable),
528
        },
529
8217239
        _ => Err(RuleNotApplicable),
530
    }
531
8217307
}
532

            
533
#[register_rule(("Base", 8800))]
534
8217307
fn negated_eq_to_neq(expr: &Expr, _: &Model) -> ApplicationResult {
535
8217307
    match expr {
536
68
        Not(_, a) => match a.as_ref() {
537
17
            Eq(_, b, c) if (!b.can_be_undefined() && !c.can_be_undefined()) => {
538
                Ok(Reduction::pure(Neq(Metadata::new(), b.clone(), c.clone())))
539
            }
540
68
            _ => Err(RuleNotApplicable),
541
        },
542
8217239
        _ => Err(RuleNotApplicable),
543
    }
544
8217307
}