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
7250565
fn remove_empty_expression(expr: &Expr, _: &Model) -> ApplicationResult {
34
    // excluded expressions
35
1837800
    if matches!(
36
5412165
        expr,
37
        FactorE(_, Reference(_,)) | FactorE(_, Literal(_)) | WatchedLiteral(_, _, _)
38
    ) {
39
5412765
        return Err(ApplicationError::RuleNotApplicable);
40
1837800
    }
41
1837800

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

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

            
51
45
    Ok(Reduction::pure(new_expr))
52
7250565
}
53

            
54
/**
55
 * Unwrap trivial sums:
56
 * ```text
57
1
 * sum([a]) = a
58
1
 * ```
59
1
 */
60
#[register_rule(("Base", 8800))]
61
7250595
fn unwrap_sum(expr: &Expr, _: &Model) -> ApplicationResult {
62
180
    match expr {
63
180
        Sum(_, exprs) if (exprs.len() == 1) => Ok(Reduction::pure(exprs[0].clone())),
64
7250565
        _ => Err(ApplicationError::RuleNotApplicable),
65
    }
66
7250595
}
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
7250565
pub fn flatten_nested_sum(expr: &Expr, _: &Model) -> ApplicationResult {
76
7250565
    match expr {
77
150
        Sum(metadata, exprs) => {
78
150
            let mut new_exprs = Vec::new();
79
150
            let mut changed = false;
80
570
            for e in exprs {
81
420
                match e {
82
150
                    Sum(_, sub_exprs) => {
83
150
                        changed = true;
84
450
                        for e in sub_exprs {
85
300
                            new_exprs.push(e.clone());
86
300
                        }
87
                    }
88
270
                    _ => new_exprs.push(e.clone()),
89
                }
90
            }
91
150
            if !changed {
92
                return Err(ApplicationError::RuleNotApplicable);
93
150
            }
94
150
            Ok(Reduction::pure(Sum(metadata.clone_dirty(), new_exprs)))
95
        }
96
7250415
        _ => Err(ApplicationError::RuleNotApplicable),
97
    }
98
7250565
}
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
7250595
fn unwrap_nested_or(expr: &Expr, _: &Model) -> ApplicationResult {
109
7250595
    match expr {
110
377940
        Or(metadata, exprs) => {
111
377940
            let mut new_exprs = Vec::new();
112
377940
            let mut changed = false;
113
1846830
            for e in exprs {
114
1468890
                match e {
115
75
                    Or(_, exprs) => {
116
75
                        changed = true;
117
225
                        for e in exprs {
118
150
                            new_exprs.push(e.clone());
119
150
                        }
120
                    }
121
1468815
                    _ => new_exprs.push(e.clone()),
122
                }
123
            }
124
377940
            if !changed {
125
377865
                return Err(ApplicationError::RuleNotApplicable);
126
75
            }
127
75
            Ok(Reduction::pure(Or(metadata.clone_dirty(), new_exprs)))
128
        }
129
6872655
        _ => Err(ApplicationError::RuleNotApplicable),
130
    }
131
7250595
}
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
7250595
fn unwrap_nested_and(expr: &Expr, _: &Model) -> ApplicationResult {
142
7250595
    match expr {
143
16785
        And(metadata, exprs) => {
144
16785
            let mut new_exprs = Vec::new();
145
16785
            let mut changed = false;
146
541740
            for e in exprs {
147
524955
                match e {
148
3720
                    And(_, exprs) => {
149
3720
                        changed = true;
150
11220
                        for e in exprs {
151
7500
                            new_exprs.push(e.clone());
152
7500
                        }
153
                    }
154
521235
                    _ => new_exprs.push(e.clone()),
155
                }
156
            }
157
16785
            if !changed {
158
13065
                return Err(ApplicationError::RuleNotApplicable);
159
3720
            }
160
3720
            Ok(Reduction::pure(And(metadata.clone_dirty(), new_exprs)))
161
        }
162
7233810
        _ => Err(ApplicationError::RuleNotApplicable),
163
    }
164
7250595
}
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
7250580
fn remove_double_negation(expr: &Expr, _: &Model) -> ApplicationResult {
175
7250580
    match expr {
176
75
        Not(_, contents) => match contents.as_ref() {
177
15
            Not(_, expr_box) => Ok(Reduction::pure(*expr_box.clone())),
178
60
            _ => Err(ApplicationError::RuleNotApplicable),
179
        },
180
7250505
        _ => Err(ApplicationError::RuleNotApplicable),
181
    }
182
7250580
}
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
7250580
fn remove_trivial_and(expr: &Expr, _: &Model) -> ApplicationResult {
192
7250580
    match expr {
193
16770
        And(_, exprs) => {
194
16770
            if exprs.len() == 1 {
195
15
                return Ok(Reduction::pure(exprs[0].clone()));
196
16755
            }
197
16755
            Err(ApplicationError::RuleNotApplicable)
198
        }
199
7233810
        _ => Err(ApplicationError::RuleNotApplicable),
200
    }
201
7250580
}
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
7250580
fn remove_trivial_or(expr: &Expr, _: &Model) -> ApplicationResult {
211
377925
    match expr {
212
377925
        // do not conflict with unwrap_nested_or rule.
213
377925
        Or(_, exprs) if exprs.len() == 1 && !matches!(exprs[0], Or(_, _)) => {
214
45
            Ok(Reduction::pure(exprs[0].clone()))
215
        }
216
7250535
        _ => Err(ApplicationError::RuleNotApplicable),
217
    }
218
7250580
}
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
7250565
fn remove_constants_from_or(expr: &Expr, _: &Model) -> ApplicationResult {
230
7250565
    match expr {
231
377910
        Or(metadata, exprs) => {
232
377910
            let mut new_exprs = Vec::new();
233
377910
            let mut changed = false;
234
1846710
            for e in exprs {
235
60
                match e {
236
60
                    FactorE(metadata, Literal(Bool(val))) => {
237
60
                        if *val {
238
                            // If we find a true, the whole expression is true
239
30
                            return Ok(Reduction::pure(FactorE(
240
30
                                metadata.clone_dirty(),
241
30
                                Literal(Bool(true)),
242
30
                            )));
243
30
                        } else {
244
30
                            // If we find a false, we can ignore it
245
30
                            changed = true;
246
30
                        }
247
                    }
248
1468770
                    _ => new_exprs.push(e.clone()),
249
                }
250
            }
251
377880
            if !changed {
252
377850
                return Err(ApplicationError::RuleNotApplicable);
253
30
            }
254
30
            Ok(Reduction::pure(Or(metadata.clone_dirty(), new_exprs)))
255
        }
256
6872655
        _ => Err(ApplicationError::RuleNotApplicable),
257
    }
258
7250565
}
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
7250565
fn remove_constants_from_and(expr: &Expr, _: &Model) -> ApplicationResult {
269
7250565
    match expr {
270
16755
        And(metadata, exprs) => {
271
16755
            let mut new_exprs = Vec::new();
272
16755
            let mut changed = false;
273
541650
            for e in exprs {
274
60
                match e {
275
60
                    FactorE(metadata, Literal(Bool(val))) => {
276
60
                        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
60
                        } else {
283
60
                            // If we find a true, we can ignore it
284
60
                            changed = true;
285
60
                        }
286
                    }
287
524835
                    _ => new_exprs.push(e.clone()),
288
                }
289
            }
290
16755
            if !changed {
291
16695
                return Err(ApplicationError::RuleNotApplicable);
292
60
            }
293
60
            Ok(Reduction::pure(And(metadata.clone_dirty(), new_exprs)))
294
        }
295
7233810
        _ => Err(ApplicationError::RuleNotApplicable),
296
    }
297
7250565
}
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
7250565
fn evaluate_constant_not(expr: &Expr, _: &Model) -> ApplicationResult {
308
7250565
    match expr {
309
60
        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
60
            _ => Err(ApplicationError::RuleNotApplicable),
315
        },
316
7250505
        _ => Err(ApplicationError::RuleNotApplicable),
317
    }
318
7250565
}
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
7250565
fn min_to_var(expr: &Expr, mdl: &Model) -> ApplicationResult {
328
7250565
    match expr {
329
105
        Min(metadata, exprs) => {
330
105
            let new_name = mdl.gensym();
331
105

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

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

            
354
105
            Ok(Reduction::new(
355
105
                FactorE(Metadata::new(), Reference(new_name)),
356
105
                And(metadata.clone_dirty(), new_top),
357
105
                new_vars,
358
105
            ))
359
        }
360
7250460
        _ => Err(ApplicationError::RuleNotApplicable),
361
    }
362
7250565
}
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
7250565
fn max_to_var(expr: &Expr, mdl: &Model) -> ApplicationResult {
372
7250565
    match expr {
373
60
        Max(metadata, exprs) => {
374
60
            let new_name = mdl.gensym();
375
60

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

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

            
398
60
            Ok(Reduction::new(
399
60
                FactorE(Metadata::new(), Reference(new_name)),
400
60
                And(metadata.clone_dirty(), new_top),
401
60
                new_vars,
402
60
            ))
403
        }
404
7250505
        _ => Err(ApplicationError::RuleNotApplicable),
405
    }
406
7250565
}
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
7250580
fn distribute_or_over_and(expr: &Expr, _: &Model) -> ApplicationResult {
417
377925
    fn find_and(exprs: &[Expr]) -> Option<usize> {
418
        // ToDo: may be better to move this to some kind of utils module?
419
1461405
        for (i, e) in exprs.iter().enumerate() {
420
1461405
            if let And(_, _) = e {
421
3480
                return Some(i);
422
1457925
            }
423
        }
424
374445
        None
425
377925
    }
426

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

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

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

            
444
3480
                        Ok(Reduction::pure(And(
445
3480
                            metadata.clone_dirty(),
446
3480
                            new_and_contents,
447
3480
                        )))
448
                    }
449
                    _ => Err(ApplicationError::RuleNotApplicable),
450
                }
451
            }
452
374445
            None => Err(ApplicationError::RuleNotApplicable),
453
        },
454
6872655
        _ => Err(ApplicationError::RuleNotApplicable),
455
    }
456
7250580
}
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
7250595
fn distribute_not_over_and(expr: &Expr, _: &Model) -> ApplicationResult {
467
7250595
    match expr {
468
90
        Not(_, contents) => match contents.as_ref() {
469
15
            And(metadata, exprs) => {
470
15
                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
15
                }
477
15
                let mut new_exprs = Vec::new();
478
45
                for e in exprs {
479
30
                    new_exprs.push(Not(metadata.clone(), Box::new(e.clone())));
480
30
                }
481
15
                Ok(Reduction::pure(Or(metadata.clone(), new_exprs)))
482
            }
483
75
            _ => Err(ApplicationError::RuleNotApplicable),
484
        },
485
7250505
        _ => Err(ApplicationError::RuleNotApplicable),
486
    }
487
7250595
}
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
7250595
fn distribute_not_over_or(expr: &Expr, _: &Model) -> ApplicationResult {
498
7250595
    match expr {
499
90
        Not(_, contents) => match contents.as_ref() {
500
15
            Or(metadata, exprs) => {
501
15
                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
15
                }
508
15
                let mut new_exprs = Vec::new();
509
45
                for e in exprs {
510
30
                    new_exprs.push(Not(metadata.clone(), Box::new(e.clone())));
511
30
                }
512
15
                Ok(Reduction::pure(And(metadata.clone(), new_exprs)))
513
            }
514
75
            _ => Err(ApplicationError::RuleNotApplicable),
515
        },
516
7250505
        _ => Err(ApplicationError::RuleNotApplicable),
517
    }
518
7250595
}
519

            
520
#[register_rule(("Base", 8800))]
521
7250565
fn negated_neq_to_eq(expr: &Expr, _: &Model) -> ApplicationResult {
522
7250565
    match expr {
523
60
        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
60
            _ => Err(RuleNotApplicable),
528
        },
529
7250505
        _ => Err(RuleNotApplicable),
530
    }
531
7250565
}
532

            
533
#[register_rule(("Base", 8800))]
534
7250565
fn negated_eq_to_neq(expr: &Expr, _: &Model) -> ApplicationResult {
535
7250565
    match expr {
536
60
        Not(_, a) => match a.as_ref() {
537
15
            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
60
            _ => Err(RuleNotApplicable),
541
        },
542
7250505
        _ => Err(RuleNotApplicable),
543
    }
544
7250565
}