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
8229377
fn remove_empty_expression(expr: &Expr, _: &Model) -> ApplicationResult {
32
    // excluded expressions
33
2090592
    if matches!(
34
8229377
        expr,
35
        Atomic(_, _)
36
            | WatchedLiteral(_, _, _)
37
            | DivEqUndefZero(_, _, _, _)
38
            | ModuloEqUndefZero(_, _, _, _)
39
    ) {
40
6138785
        return Err(ApplicationError::RuleNotApplicable);
41
2090592
    }
42
2090592

            
43
2090592
    if !expr.children().is_empty() {
44
2090541
        return Err(ApplicationError::RuleNotApplicable);
45
51
    }
46

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

            
52
51
    Ok(Reduction::pure(new_expr))
53
8229377
}
54

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

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

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

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

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

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

            
167
/**
168
* Remove double negation:
169

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
462
* ```text
463
1
* not(and(a, b)) = or(not a, not b)
464
1
* ```
465
1
 */
466
#[register_rule(("Base", 8400))]
467
8229411
fn distribute_not_over_and(expr: &Expr, _: &Model) -> ApplicationResult {
468
34268413
    for child in expr.universe() {
469
34265217
        if matches!(
470
34268413
            child,
471
            Expr::UnsafeDiv(_, _, _) | Expr::Bubble(_, _, _) | Expr::UnsafeMod(_, _, _)
472
        ) {
473
3196
            return Err(RuleNotApplicable);
474
34265217
        }
475
    }
476
8226215
    match expr {
477
544
        Not(_, contents) => match contents.as_ref() {
478
255
            And(metadata, exprs) => {
479
255
                if exprs.len() == 1 {
480
102
                    let single_expr = exprs[0].clone();
481
102
                    return Ok(Reduction::pure(Not(
482
102
                        Metadata::new(),
483
102
                        Box::new(single_expr.clone()),
484
102
                    )));
485
153
                }
486
153
                let mut new_exprs = Vec::new();
487
459
                for e in exprs {
488
306
                    new_exprs.push(Not(metadata.clone(), Box::new(e.clone())));
489
306
                }
490
153
                Ok(Reduction::pure(Or(metadata.clone(), new_exprs)))
491
            }
492
289
            _ => Err(ApplicationError::RuleNotApplicable),
493
        },
494
8225671
        _ => Err(ApplicationError::RuleNotApplicable),
495
    }
496
8229411
}
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
8229411
fn distribute_not_over_or(expr: &Expr, _: &Model) -> ApplicationResult {
507
8229411
    match expr {
508
1054
        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
1037
            _ => Err(ApplicationError::RuleNotApplicable),
524
        },
525
8228357
        _ => Err(ApplicationError::RuleNotApplicable),
526
    }
527
8229411
}
528

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

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