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

            
10
use Atom::*;
11
use Expr::*;
12
use Lit::*;
13

            
14
/*****************************************************************************/
15
/*        This file contains basic rules for simplifying expressions         */
16
/*****************************************************************************/
17

            
18
register_rule_set!("Base", 100, ());
19

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

            
46
2086087
    if !expr.children().is_empty() {
47
2086036
        return Err(ApplicationError::RuleNotApplicable);
48
51
    }
49

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

            
55
51
    Ok(Reduction::pure(new_expr))
56
8222390
}
57

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

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

            
104
/**
105
* Unwrap nested `or`
106

            
107
* ```text
108
1
* or(or(a, b), c) = or(a, b, c)
109
1
* ```
110
1
 */
111
#[register_rule(("Base", 8800))]
112
8222424
fn unwrap_nested_or(expr: &Expr, _: &Model) -> ApplicationResult {
113
8222424
    match expr {
114
428740
        Or(metadata, exprs) => {
115
428740
            let mut new_exprs = Vec::new();
116
428740
            let mut changed = false;
117
2094468
            for e in exprs {
118
1665728
                match e {
119
102
                    Or(_, exprs) => {
120
102
                        changed = true;
121
306
                        for e in exprs {
122
204
                            new_exprs.push(e.clone());
123
204
                        }
124
                    }
125
1665626
                    _ => new_exprs.push(e.clone()),
126
                }
127
            }
128
428740
            if !changed {
129
428638
                return Err(ApplicationError::RuleNotApplicable);
130
102
            }
131
102
            Ok(Reduction::pure(Or(metadata.clone_dirty(), new_exprs)))
132
        }
133
7793684
        _ => Err(ApplicationError::RuleNotApplicable),
134
    }
135
8222424
}
136

            
137
/**
138
* Unwrap nested `and`
139

            
140
* ```text
141
1
* and(and(a, b), c) = and(a, b, c)
142
1
* ```
143
1
 */
144
#[register_rule(("Base", 8800))]
145
8222424
fn unwrap_nested_and(expr: &Expr, _: &Model) -> ApplicationResult {
146
8222424
    match expr {
147
19788
        And(metadata, exprs) => {
148
19788
            let mut new_exprs = Vec::new();
149
19788
            let mut changed = false;
150
616998
            for e in exprs {
151
597210
                match e {
152
4369
                    And(_, exprs) => {
153
4369
                        changed = true;
154
13056
                        for e in exprs {
155
8687
                            new_exprs.push(e.clone());
156
8687
                        }
157
                    }
158
592841
                    _ => new_exprs.push(e.clone()),
159
                }
160
            }
161
19788
            if !changed {
162
15419
                return Err(ApplicationError::RuleNotApplicable);
163
4369
            }
164
4369
            Ok(Reduction::pure(And(metadata.clone_dirty(), new_exprs)))
165
        }
166
8202636
        _ => Err(ApplicationError::RuleNotApplicable),
167
    }
168
8222424
}
169

            
170
/**
171
* Remove double negation:
172

            
173
* ```text
174
1
* not(not(a)) = a
175
1
* ```
176
1
 */
177
#[register_rule(("Base", 8400))]
178
8222407
fn remove_double_negation(expr: &Expr, _: &Model) -> ApplicationResult {
179
8222407
    match expr {
180
544
        Not(_, contents) => match contents.as_ref() {
181
17
            Not(_, expr_box) => Ok(Reduction::pure(*expr_box.clone())),
182
527
            _ => Err(ApplicationError::RuleNotApplicable),
183
        },
184
8221863
        _ => Err(ApplicationError::RuleNotApplicable),
185
    }
186
8222407
}
187

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

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

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

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

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

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

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

            
352
119
            let mut new_vars = SymbolTable::new();
353
119
            let domain = expr
354
119
                .domain_of(&mdl.variables)
355
119
                .ok_or(ApplicationError::DomainError)?;
356
119
            new_vars.insert(new_name.clone(), DecisionVariable::new(domain));
357
119

            
358
119
            Ok(Reduction::new(
359
119
                Atomic(Metadata::new(), Reference(new_name)),
360
119
                And(metadata.clone_dirty(), new_top),
361
119
                new_vars,
362
119
            ))
363
        }
364
8222271
        _ => Err(ApplicationError::RuleNotApplicable),
365
    }
366
8222390
}
367

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

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

            
396
68
            let mut new_vars = SymbolTable::new();
397
68
            let domain = expr
398
68
                .domain_of(&mdl.variables)
399
68
                .ok_or(ApplicationError::DomainError)?;
400
68
            new_vars.insert(new_name.clone(), DecisionVariable::new(domain));
401
68

            
402
68
            Ok(Reduction::new(
403
68
                Atomic(Metadata::new(), Reference(new_name)),
404
68
                And(metadata.clone_dirty(), new_top),
405
68
                new_vars,
406
68
            ))
407
        }
408
8222322
        _ => Err(ApplicationError::RuleNotApplicable),
409
    }
410
8222390
}
411

            
412
/**
413
* Apply the Distributive Law to expressions like `Or([..., And(a, b)])`
414

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

            
431
8222407
    match expr {
432
428723
        Or(_, exprs) => match find_and(exprs) {
433
3944
            Some(idx) => {
434
3944
                let mut rest = exprs.clone();
435
3944
                let and_expr = rest.remove(idx);
436
3944

            
437
3944
                match and_expr {
438
3944
                    And(metadata, and_exprs) => {
439
3944
                        let mut new_and_contents = Vec::new();
440

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

            
448
3944
                        Ok(Reduction::pure(And(
449
3944
                            metadata.clone_dirty(),
450
3944
                            new_and_contents,
451
3944
                        )))
452
                    }
453
                    _ => Err(ApplicationError::RuleNotApplicable),
454
                }
455
            }
456
424779
            None => Err(ApplicationError::RuleNotApplicable),
457
        },
458
7793684
        _ => Err(ApplicationError::RuleNotApplicable),
459
    }
460
8222407
}
461

            
462
/**
463
* Distribute `not` over `and` (De Morgan's Law):
464

            
465
* ```text
466
1
* not(and(a, b)) = or(not a, not b)
467
1
* ```
468
1
 */
469
#[register_rule(("Base", 8400))]
470
8222424
fn distribute_not_over_and(expr: &Expr, _: &Model) -> ApplicationResult {
471
34239853
    for child in expr.universe() {
472
34239853
        if matches!(child, Expr::UnsafeDiv(_, _, _) | Expr::Bubble(_, _, _)) {
473
1598
            return Err(RuleNotApplicable);
474
34238255
        }
475
    }
476
8220826
    match expr {
477
306
        Not(_, contents) => match contents.as_ref() {
478
136
            And(metadata, exprs) => {
479
136
                if exprs.len() == 1 {
480
51
                    let single_expr = exprs[0].clone();
481
51
                    return Ok(Reduction::pure(Not(
482
51
                        Metadata::new(),
483
51
                        Box::new(single_expr.clone()),
484
51
                    )));
485
85
                }
486
85
                let mut new_exprs = Vec::new();
487
255
                for e in exprs {
488
170
                    new_exprs.push(Not(metadata.clone(), Box::new(e.clone())));
489
170
                }
490
85
                Ok(Reduction::pure(Or(metadata.clone(), new_exprs)))
491
            }
492
170
            _ => Err(ApplicationError::RuleNotApplicable),
493
        },
494
8220520
        _ => Err(ApplicationError::RuleNotApplicable),
495
    }
496
8222424
}
497

            
498
/**
499
* Distribute `not` over `or` (De Morgan's Law):
500

            
501
* ```text
502
1
* not(or(a, b)) = and(not a, not b)
503
1
* ```
504
1
 */
505
#[register_rule(("Base", 8400))]
506
8222424
fn distribute_not_over_or(expr: &Expr, _: &Model) -> ApplicationResult {
507
8222424
    match expr {
508
561
        Not(_, contents) => match contents.as_ref() {
509
17
            Or(metadata, exprs) => {
510
17
                if exprs.len() == 1 {
511
                    let single_expr = exprs[0].clone();
512
                    return Ok(Reduction::pure(Not(
513
                        Metadata::new(),
514
                        Box::new(single_expr.clone()),
515
                    )));
516
17
                }
517
17
                let mut new_exprs = Vec::new();
518
51
                for e in exprs {
519
34
                    new_exprs.push(Not(metadata.clone(), Box::new(e.clone())));
520
34
                }
521
17
                Ok(Reduction::pure(And(metadata.clone(), new_exprs)))
522
            }
523
544
            _ => Err(ApplicationError::RuleNotApplicable),
524
        },
525
8221863
        _ => Err(ApplicationError::RuleNotApplicable),
526
    }
527
8222424
}
528

            
529
#[register_rule(("Base", 8800))]
530
8222390
fn negated_neq_to_eq(expr: &Expr, _: &Model) -> ApplicationResult {
531
8222390
    match expr {
532
527
        Not(_, a) => match a.as_ref() {
533
68
            Neq(_, b, c) if (!b.can_be_undefined() && !c.can_be_undefined()) => {
534
68
                Ok(Reduction::pure(Eq(Metadata::new(), b.clone(), c.clone())))
535
            }
536
459
            _ => Err(RuleNotApplicable),
537
        },
538
8221863
        _ => Err(RuleNotApplicable),
539
    }
540
8222390
}
541

            
542
#[register_rule(("Base", 8800))]
543
8222390
fn negated_eq_to_neq(expr: &Expr, _: &Model) -> ApplicationResult {
544
8222390
    match expr {
545
527
        Not(_, a) => match a.as_ref() {
546
170
            Eq(_, b, c) if (!b.can_be_undefined() && !c.can_be_undefined()) => {
547
51
                Ok(Reduction::pure(Neq(Metadata::new(), b.clone(), c.clone())))
548
            }
549
476
            _ => Err(RuleNotApplicable),
550
        },
551
8221863
        _ => Err(RuleNotApplicable),
552
    }
553
8222390
}