1use super::TLExpr;
22
23pub fn to_nnf(expr: &TLExpr) -> TLExpr {
45 match expr {
46 TLExpr::Pred { .. } => expr.clone(),
48 TLExpr::Constant(_) => expr.clone(),
49
50 TLExpr::And(l, r) => TLExpr::and(to_nnf(l), to_nnf(r)),
52 TLExpr::Or(l, r) => TLExpr::or(to_nnf(l), to_nnf(r)),
53
54 TLExpr::Imply(l, r) => {
56 let not_l = push_negation_inward(&TLExpr::negate((**l).clone()));
57 let r_nnf = to_nnf(r);
58 TLExpr::or(not_l, r_nnf)
59 }
60
61 TLExpr::Not(inner) => push_negation_inward(&TLExpr::Not(Box::new((**inner).clone()))),
63
64 TLExpr::Exists { var, domain, body } => TLExpr::exists(var, domain, to_nnf(body)),
66 TLExpr::ForAll { var, domain, body } => TLExpr::forall(var, domain, to_nnf(body)),
67
68 TLExpr::Score(inner) => TLExpr::score(to_nnf(inner)),
70
71 TLExpr::Box(inner) => TLExpr::modal_box(to_nnf(inner)),
73 TLExpr::Diamond(inner) => TLExpr::modal_diamond(to_nnf(inner)),
74 TLExpr::Next(inner) => TLExpr::next(to_nnf(inner)),
75 TLExpr::Eventually(inner) => TLExpr::eventually(to_nnf(inner)),
76 TLExpr::Always(inner) => TLExpr::always(to_nnf(inner)),
77 TLExpr::Until { before, after } => TLExpr::until(to_nnf(before), to_nnf(after)),
78
79 TLExpr::Add(l, r) => TLExpr::add(to_nnf(l), to_nnf(r)),
82 TLExpr::Sub(l, r) => TLExpr::sub(to_nnf(l), to_nnf(r)),
83 TLExpr::Mul(l, r) => TLExpr::mul(to_nnf(l), to_nnf(r)),
84 TLExpr::Div(l, r) => TLExpr::div(to_nnf(l), to_nnf(r)),
85 TLExpr::Pow(l, r) => TLExpr::pow(to_nnf(l), to_nnf(r)),
86 TLExpr::Mod(l, r) => TLExpr::modulo(to_nnf(l), to_nnf(r)),
87 TLExpr::Min(l, r) => TLExpr::min(to_nnf(l), to_nnf(r)),
88 TLExpr::Max(l, r) => TLExpr::max(to_nnf(l), to_nnf(r)),
89
90 TLExpr::Abs(e) => TLExpr::abs(to_nnf(e)),
92 TLExpr::Floor(e) => TLExpr::floor(to_nnf(e)),
93 TLExpr::Ceil(e) => TLExpr::ceil(to_nnf(e)),
94 TLExpr::Round(e) => TLExpr::round(to_nnf(e)),
95 TLExpr::Sqrt(e) => TLExpr::sqrt(to_nnf(e)),
96 TLExpr::Exp(e) => TLExpr::exp(to_nnf(e)),
97 TLExpr::Log(e) => TLExpr::log(to_nnf(e)),
98 TLExpr::Sin(e) => TLExpr::sin(to_nnf(e)),
99 TLExpr::Cos(e) => TLExpr::cos(to_nnf(e)),
100 TLExpr::Tan(e) => TLExpr::tan(to_nnf(e)),
101
102 TLExpr::Eq(l, r) => TLExpr::eq(to_nnf(l), to_nnf(r)),
104 TLExpr::Lt(l, r) => TLExpr::lt(to_nnf(l), to_nnf(r)),
105 TLExpr::Gt(l, r) => TLExpr::gt(to_nnf(l), to_nnf(r)),
106 TLExpr::Lte(l, r) => TLExpr::lte(to_nnf(l), to_nnf(r)),
107 TLExpr::Gte(l, r) => TLExpr::gte(to_nnf(l), to_nnf(r)),
108
109 TLExpr::IfThenElse {
111 condition,
112 then_branch,
113 else_branch,
114 } => TLExpr::if_then_else(to_nnf(condition), to_nnf(then_branch), to_nnf(else_branch)),
115
116 TLExpr::Aggregate {
118 op,
119 var,
120 domain,
121 body,
122 group_by,
123 } => TLExpr::Aggregate {
124 op: op.clone(),
125 var: var.clone(),
126 domain: domain.clone(),
127 body: Box::new(to_nnf(body)),
128 group_by: group_by.clone(),
129 },
130 TLExpr::Let { var, value, body } => TLExpr::let_binding(var, to_nnf(value), to_nnf(body)),
131
132 TLExpr::TNorm { kind, left, right } => TLExpr::tnorm(*kind, to_nnf(left), to_nnf(right)),
134 TLExpr::TCoNorm { kind, left, right } => {
135 TLExpr::tconorm(*kind, to_nnf(left), to_nnf(right))
136 }
137 TLExpr::FuzzyNot { kind, expr } => TLExpr::fuzzy_not(*kind, to_nnf(expr)),
138 TLExpr::FuzzyImplication {
139 kind,
140 premise,
141 conclusion,
142 } => TLExpr::fuzzy_imply(*kind, to_nnf(premise), to_nnf(conclusion)),
143
144 TLExpr::SoftExists {
146 var,
147 domain,
148 body,
149 temperature,
150 } => TLExpr::soft_exists(var, domain, to_nnf(body), *temperature),
151 TLExpr::SoftForAll {
152 var,
153 domain,
154 body,
155 temperature,
156 } => TLExpr::soft_forall(var, domain, to_nnf(body), *temperature),
157 TLExpr::WeightedRule { weight, rule } => TLExpr::weighted_rule(*weight, to_nnf(rule)),
158 TLExpr::ProbabilisticChoice { alternatives } => TLExpr::probabilistic_choice(
159 alternatives.iter().map(|(p, e)| (*p, to_nnf(e))).collect(),
160 ),
161
162 TLExpr::Release { released, releaser } => {
164 TLExpr::release(to_nnf(released), to_nnf(releaser))
165 }
166 TLExpr::WeakUntil { before, after } => TLExpr::weak_until(to_nnf(before), to_nnf(after)),
167 TLExpr::StrongRelease { released, releaser } => {
168 TLExpr::strong_release(to_nnf(released), to_nnf(releaser))
169 }
170
171 TLExpr::Lambda {
173 var,
174 var_type,
175 body,
176 } => TLExpr::lambda(var.clone(), var_type.clone(), to_nnf(body)),
177 TLExpr::Apply { function, argument } => TLExpr::apply(to_nnf(function), to_nnf(argument)),
178 TLExpr::SetMembership { element, set } => {
179 TLExpr::set_membership(to_nnf(element), to_nnf(set))
180 }
181 TLExpr::SetUnion { left, right } => TLExpr::set_union(to_nnf(left), to_nnf(right)),
182 TLExpr::SetIntersection { left, right } => {
183 TLExpr::set_intersection(to_nnf(left), to_nnf(right))
184 }
185 TLExpr::SetDifference { left, right } => {
186 TLExpr::set_difference(to_nnf(left), to_nnf(right))
187 }
188 TLExpr::SetCardinality { set } => TLExpr::set_cardinality(to_nnf(set)),
189 TLExpr::EmptySet => expr.clone(),
190 TLExpr::SetComprehension {
191 var,
192 domain,
193 condition,
194 } => TLExpr::set_comprehension(var.clone(), domain.clone(), to_nnf(condition)),
195 TLExpr::CountingExists {
196 var,
197 domain,
198 body,
199 min_count,
200 } => TLExpr::counting_exists(var.clone(), domain.clone(), to_nnf(body), *min_count),
201 TLExpr::CountingForAll {
202 var,
203 domain,
204 body,
205 min_count,
206 } => TLExpr::counting_forall(var.clone(), domain.clone(), to_nnf(body), *min_count),
207 TLExpr::ExactCount {
208 var,
209 domain,
210 body,
211 count,
212 } => TLExpr::exact_count(var.clone(), domain.clone(), to_nnf(body), *count),
213 TLExpr::Majority { var, domain, body } => {
214 TLExpr::majority(var.clone(), domain.clone(), to_nnf(body))
215 }
216 TLExpr::LeastFixpoint { var, body } => TLExpr::least_fixpoint(var.clone(), to_nnf(body)),
217 TLExpr::GreatestFixpoint { var, body } => {
218 TLExpr::greatest_fixpoint(var.clone(), to_nnf(body))
219 }
220 TLExpr::Nominal { .. } => expr.clone(),
221 TLExpr::At { nominal, formula } => TLExpr::at(nominal.clone(), to_nnf(formula)),
222 TLExpr::Somewhere { formula } => TLExpr::somewhere(to_nnf(formula)),
223 TLExpr::Everywhere { formula } => TLExpr::everywhere(to_nnf(formula)),
224 TLExpr::AllDifferent { .. } => expr.clone(),
225 TLExpr::GlobalCardinality {
226 variables,
227 values,
228 min_occurrences,
229 max_occurrences,
230 } => TLExpr::global_cardinality(
231 variables.clone(),
232 values.iter().map(to_nnf).collect(),
233 min_occurrences.clone(),
234 max_occurrences.clone(),
235 ),
236 TLExpr::Abducible { .. } => expr.clone(),
237 TLExpr::Explain { formula } => TLExpr::explain(to_nnf(formula)),
238 }
239}
240
241fn push_negation_inward(expr: &TLExpr) -> TLExpr {
243 match expr {
244 TLExpr::Not(inner) => match &**inner {
246 TLExpr::Not(inner2) => to_nnf(inner2),
247 TLExpr::And(l, r) => {
249 let not_l = push_negation_inward(&TLExpr::negate((**l).clone()));
251 let not_r = push_negation_inward(&TLExpr::negate((**r).clone()));
252 TLExpr::or(not_l, not_r)
253 }
254 TLExpr::Or(l, r) => {
255 let not_l = push_negation_inward(&TLExpr::negate((**l).clone()));
257 let not_r = push_negation_inward(&TLExpr::negate((**r).clone()));
258 TLExpr::and(not_l, not_r)
259 }
260 TLExpr::Imply(l, r) => {
262 let l_nnf = to_nnf(l);
263 let not_r = push_negation_inward(&TLExpr::negate((**r).clone()));
264 TLExpr::and(l_nnf, not_r)
265 }
266 TLExpr::Exists { var, domain, body } => {
268 let not_body = push_negation_inward(&TLExpr::negate((**body).clone()));
269 TLExpr::forall(var, domain, not_body)
270 }
271 TLExpr::ForAll { var, domain, body } => {
272 let not_body = push_negation_inward(&TLExpr::negate((**body).clone()));
273 TLExpr::exists(var, domain, not_body)
274 }
275 _ => expr.clone(),
277 },
278 _ => to_nnf(expr),
280 }
281}
282
283pub fn to_cnf(expr: &TLExpr) -> TLExpr {
305 let nnf = to_nnf(expr);
306 cnf_distribute(&nnf)
307}
308
309fn cnf_distribute(expr: &TLExpr) -> TLExpr {
311 match expr {
312 TLExpr::And(l, r) => {
313 let l_cnf = cnf_distribute(l);
314 let r_cnf = cnf_distribute(r);
315 TLExpr::and(l_cnf, r_cnf)
316 }
317 TLExpr::Or(l, r) => {
318 let l_cnf = cnf_distribute(l);
319 let r_cnf = cnf_distribute(r);
320 distribute_or_over_and(&l_cnf, &r_cnf)
321 }
322 TLExpr::Exists { var, domain, body } => TLExpr::exists(var, domain, cnf_distribute(body)),
323 TLExpr::ForAll { var, domain, body } => TLExpr::forall(var, domain, cnf_distribute(body)),
324 _ => expr.clone(),
325 }
326}
327
328fn distribute_or_over_and(left: &TLExpr, right: &TLExpr) -> TLExpr {
332 match (left, right) {
333 (TLExpr::And(a, b), c) => {
335 let left_part = distribute_or_over_and(a, c);
336 let right_part = distribute_or_over_and(b, c);
337 TLExpr::and(left_part, right_part)
338 }
339 (a, TLExpr::And(b, c)) => {
341 let left_part = distribute_or_over_and(a, b);
342 let right_part = distribute_or_over_and(a, c);
343 TLExpr::and(left_part, right_part)
344 }
345 (a, b) => TLExpr::or(a.clone(), b.clone()),
347 }
348}
349
350pub fn to_dnf(expr: &TLExpr) -> TLExpr {
372 let nnf = to_nnf(expr);
373 dnf_distribute(&nnf)
374}
375
376fn dnf_distribute(expr: &TLExpr) -> TLExpr {
378 match expr {
379 TLExpr::Or(l, r) => {
380 let l_dnf = dnf_distribute(l);
381 let r_dnf = dnf_distribute(r);
382 TLExpr::or(l_dnf, r_dnf)
383 }
384 TLExpr::And(l, r) => {
385 let l_dnf = dnf_distribute(l);
386 let r_dnf = dnf_distribute(r);
387 distribute_and_over_or(&l_dnf, &r_dnf)
388 }
389 TLExpr::Exists { var, domain, body } => TLExpr::exists(var, domain, dnf_distribute(body)),
390 TLExpr::ForAll { var, domain, body } => TLExpr::forall(var, domain, dnf_distribute(body)),
391 _ => expr.clone(),
392 }
393}
394
395fn distribute_and_over_or(left: &TLExpr, right: &TLExpr) -> TLExpr {
399 match (left, right) {
400 (TLExpr::Or(a, b), c) => {
402 let left_part = distribute_and_over_or(a, c);
403 let right_part = distribute_and_over_or(b, c);
404 TLExpr::or(left_part, right_part)
405 }
406 (a, TLExpr::Or(b, c)) => {
408 let left_part = distribute_and_over_or(a, b);
409 let right_part = distribute_and_over_or(a, c);
410 TLExpr::or(left_part, right_part)
411 }
412 (a, b) => TLExpr::and(a.clone(), b.clone()),
414 }
415}
416
417pub fn is_cnf(expr: &TLExpr) -> bool {
422 match expr {
423 TLExpr::And(l, r) => is_cnf(l) && is_cnf(r),
424 TLExpr::Or(l, r) => is_cnf_clause(l) && is_cnf_clause(r),
425 TLExpr::Not(inner) => is_literal(inner),
426 TLExpr::Pred { .. } => true,
427 TLExpr::Exists { body, .. } | TLExpr::ForAll { body, .. } => is_cnf(body),
428 TLExpr::Constant(_) => true,
429 _ => false, }
431}
432
433fn is_cnf_clause(expr: &TLExpr) -> bool {
435 match expr {
436 TLExpr::Or(l, r) => is_cnf_clause(l) && is_cnf_clause(r),
437 TLExpr::Not(inner) => is_literal(inner),
438 TLExpr::Pred { .. } => true,
439 TLExpr::Constant(_) => true,
440 _ => false,
441 }
442}
443
444pub fn is_dnf(expr: &TLExpr) -> bool {
449 match expr {
450 TLExpr::Or(l, r) => is_dnf(l) && is_dnf(r),
451 TLExpr::And(l, r) => is_dnf_clause(l) && is_dnf_clause(r),
452 TLExpr::Not(inner) => is_literal(inner),
453 TLExpr::Pred { .. } => true,
454 TLExpr::Exists { body, .. } | TLExpr::ForAll { body, .. } => is_dnf(body),
455 TLExpr::Constant(_) => true,
456 _ => false,
457 }
458}
459
460fn is_dnf_clause(expr: &TLExpr) -> bool {
462 match expr {
463 TLExpr::And(l, r) => is_dnf_clause(l) && is_dnf_clause(r),
464 TLExpr::Not(inner) => is_literal(inner),
465 TLExpr::Pred { .. } => true,
466 TLExpr::Constant(_) => true,
467 _ => false,
468 }
469}
470
471fn is_literal(expr: &TLExpr) -> bool {
473 matches!(expr, TLExpr::Pred { .. } | TLExpr::Constant(_))
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479 use crate::Term;
480
481 fn pred_a() -> TLExpr {
482 TLExpr::pred("A", vec![])
483 }
484
485 fn pred_b() -> TLExpr {
486 TLExpr::pred("B", vec![])
487 }
488
489 fn pred_c() -> TLExpr {
490 TLExpr::pred("C", vec![])
491 }
492
493 #[test]
494 fn test_nnf_double_negation() {
495 let expr = TLExpr::negate(TLExpr::negate(pred_a()));
497 let nnf = to_nnf(&expr);
498 assert_eq!(nnf, pred_a());
499 }
500
501 #[test]
502 fn test_nnf_de_morgan_and() {
503 let expr = TLExpr::negate(TLExpr::and(pred_a(), pred_b()));
505 let nnf = to_nnf(&expr);
506 assert!(matches!(nnf, TLExpr::Or(_, _)));
507 if let TLExpr::Or(l, r) = nnf {
508 assert!(matches!(*l, TLExpr::Not(_)));
509 assert!(matches!(*r, TLExpr::Not(_)));
510 }
511 }
512
513 #[test]
514 fn test_nnf_de_morgan_or() {
515 let expr = TLExpr::negate(TLExpr::or(pred_a(), pred_b()));
517 let nnf = to_nnf(&expr);
518 assert!(matches!(nnf, TLExpr::And(_, _)));
519 if let TLExpr::And(l, r) = nnf {
520 assert!(matches!(*l, TLExpr::Not(_)));
521 assert!(matches!(*r, TLExpr::Not(_)));
522 }
523 }
524
525 #[test]
526 fn test_nnf_implication() {
527 let expr = TLExpr::imply(pred_a(), pred_b());
529 let nnf = to_nnf(&expr);
530 assert!(matches!(nnf, TLExpr::Or(_, _)));
531 }
532
533 #[test]
534 fn test_nnf_quantifier_negation() {
535 let exists_expr = TLExpr::exists("x", "D", pred_a());
537 let neg_exists = TLExpr::negate(exists_expr);
538 let nnf = to_nnf(&neg_exists);
539 assert!(matches!(nnf, TLExpr::ForAll { .. }));
540
541 let forall_expr = TLExpr::forall("x", "D", pred_a());
543 let neg_forall = TLExpr::negate(forall_expr);
544 let nnf = to_nnf(&neg_forall);
545 assert!(matches!(nnf, TLExpr::Exists { .. }));
546 }
547
548 #[test]
549 fn test_cnf_simple_distribution() {
550 let expr = TLExpr::or(TLExpr::and(pred_a(), pred_b()), pred_c());
552 let cnf = to_cnf(&expr);
553
554 assert!(is_cnf(&cnf));
556 assert!(matches!(cnf, TLExpr::And(_, _)));
558 }
559
560 #[test]
561 fn test_cnf_already_in_cnf() {
562 let expr = TLExpr::and(TLExpr::or(pred_a(), pred_b()), pred_c());
564 let cnf = to_cnf(&expr);
565 assert!(is_cnf(&cnf));
566 }
567
568 #[test]
569 fn test_dnf_simple_distribution() {
570 let expr = TLExpr::and(TLExpr::or(pred_a(), pred_b()), pred_c());
572 let dnf = to_dnf(&expr);
573
574 assert!(is_dnf(&dnf));
576 assert!(matches!(dnf, TLExpr::Or(_, _)));
578 }
579
580 #[test]
581 fn test_dnf_already_in_dnf() {
582 let expr = TLExpr::or(TLExpr::and(pred_a(), pred_b()), pred_c());
584 let dnf = to_dnf(&expr);
585 assert!(is_dnf(&dnf));
586 }
587
588 #[test]
589 fn test_is_cnf() {
590 let d = TLExpr::pred("D", vec![]);
592 let expr = TLExpr::and(
593 TLExpr::or(pred_a(), pred_b()),
594 TLExpr::or(pred_c(), TLExpr::negate(d)),
595 );
596 assert!(is_cnf(&expr));
597
598 let not_cnf = TLExpr::or(TLExpr::and(pred_a(), pred_b()), pred_c());
600 assert!(!is_cnf(¬_cnf));
601 }
602
603 #[test]
604 fn test_is_dnf() {
605 let d = TLExpr::pred("D", vec![]);
607 let expr = TLExpr::or(
608 TLExpr::and(pred_a(), pred_b()),
609 TLExpr::and(pred_c(), TLExpr::negate(d)),
610 );
611 assert!(is_dnf(&expr));
612
613 let not_dnf = TLExpr::and(TLExpr::or(pred_a(), pred_b()), pred_c());
615 assert!(!is_dnf(¬_dnf));
616 }
617
618 #[test]
619 fn test_complex_cnf_conversion() {
620 let expr = TLExpr::or(
622 TLExpr::and(TLExpr::or(pred_a(), pred_b()), pred_c()),
623 TLExpr::pred("D", vec![]),
624 );
625 let cnf = to_cnf(&expr);
626 assert!(is_cnf(&cnf));
627 }
628
629 #[test]
630 fn test_complex_dnf_conversion() {
631 let expr = TLExpr::and(
633 TLExpr::or(TLExpr::and(pred_a(), pred_b()), pred_c()),
634 TLExpr::pred("D", vec![]),
635 );
636 let dnf = to_dnf(&expr);
637 assert!(is_dnf(&dnf));
638 }
639
640 #[test]
641 fn test_cnf_with_negations() {
642 let expr = TLExpr::or(
644 TLExpr::negate(pred_a()),
645 TLExpr::and(pred_b(), TLExpr::negate(pred_c())),
646 );
647 let cnf = to_cnf(&expr);
648 assert!(is_cnf(&cnf));
649 }
650
651 #[test]
652 fn test_dnf_with_negations() {
653 let expr = TLExpr::and(
655 TLExpr::negate(pred_a()),
656 TLExpr::or(pred_b(), TLExpr::negate(pred_c())),
657 );
658 let dnf = to_dnf(&expr);
659 assert!(is_dnf(&dnf));
660 }
661
662 #[test]
663 fn test_cnf_with_quantifiers() {
664 let pred_ax = TLExpr::pred("A", vec![Term::var("x")]);
666 let pred_bx = TLExpr::pred("B", vec![Term::var("x")]);
667 let expr = TLExpr::forall("x", "D", TLExpr::or(pred_ax, pred_bx));
668 let cnf = to_cnf(&expr);
669 assert!(matches!(cnf, TLExpr::ForAll { .. }));
670 }
671
672 #[test]
673 fn test_literal_identification() {
674 assert!(is_literal(&pred_a()));
675 assert!(is_literal(&TLExpr::constant(42.0)));
676 assert!(!is_literal(&TLExpr::and(pred_a(), pred_b())));
677 assert!(!is_literal(&TLExpr::negate(pred_a())));
678 }
679}