1#![allow(clippy::unnecessary_box_returns, clippy::used_underscore_items)]
2mod latch_ff;
6pub mod logic;
7mod parser;
8use crate::{
9 Ctx, DefaultCtx,
10 ast::{CodeFormatter, Indentation, ParseScope, ParsingBuilder},
11 cell::CellCtx as _,
12};
13pub use latch_ff::{ClearPresetState, FF, FFBank, Latch, LatchBank, LatchFF};
14use parser::BoolExprErr;
15
16pub use biodivine_lib_bdd::{
17 Bdd, BddVariableSet, boolean_expression::BooleanExpression as Expr,
18};
19use core::{
20 borrow::Borrow,
21 cmp::Ordering,
22 fmt::{self, Write},
23 ops::{Deref, DerefMut},
24 str::FromStr as _,
25};
26use std::{collections::HashSet, sync::LazyLock};
27
28static UNKNOWN: LazyLock<Box<Expr>> =
29 LazyLock::new(|| Box::new(Expr::Variable("_unknown_".to_owned())));
30
31pub trait BooleanExpressionLike: Borrow<Expr> + Into<Expr> {
32 #[inline]
34 fn previous(&self) -> Expr {
35 let mut expr: Expr = self.borrow().clone();
36 _previous(&mut expr);
37 expr
38 }
39}
40
41impl BooleanExpressionLike for Expr {}
42impl BooleanExpressionLike for BooleanExpression {}
43impl BooleanExpressionLike for BddBooleanExpression {}
44
45#[derive(Debug, Clone)]
71#[derive(serde::Serialize, serde::Deserialize)]
72pub struct BooleanExpression {
73 pub expr: Expr,
75}
76
77impl Borrow<Expr> for BooleanExpression {
78 #[inline]
79 fn borrow(&self) -> &Expr {
80 &self.expr
81 }
82}
83impl From<Expr> for BooleanExpression {
84 #[inline]
85 fn from(expr: Expr) -> Self {
86 Self { expr }
87 }
88}
89impl From<BooleanExpression> for Expr {
90 #[inline]
91 fn from(val: BooleanExpression) -> Self {
92 val.expr
93 }
94}
95
96crate::ast::impl_self_builder!(BooleanExpression);
97impl<C: 'static + Ctx> crate::ast::SimpleAttri<C> for BooleanExpression {
98 #[inline]
99 fn nom_parse<'a>(
100 i: &'a str,
101 scope: &mut ParseScope<'_>,
102 ) -> crate::ast::SimpleParseRes<'a, Self> {
103 crate::ast::parser::simple_custom(
104 i,
105 &mut scope.loc.line_num,
106 Self::parse,
107 Self::unquote,
108 )
109 }
110 #[inline]
111 fn fmt_self<T: Write, I: Indentation>(
112 &self,
113 f: &mut CodeFormatter<'_, T, I>,
114 ) -> fmt::Result {
115 write!(f, "\"{self}\"")
116 }
117}
118impl<C: 'static + Ctx> crate::ast::SimpleAttri<C> for LogicBooleanExpression {
119 #[inline]
120 fn nom_parse<'a>(
121 i: &'a str,
122 scope: &mut ParseScope<'_>,
123 ) -> crate::ast::SimpleParseRes<'a, Self::Builder> {
124 <Self::Builder as crate::ast::SimpleAttri<DefaultCtx>>::nom_parse(i, scope)
125 }
126 #[inline]
127 fn fmt_self<T: Write, I: Indentation>(
128 &self,
129 f: &mut CodeFormatter<'_, T, I>,
130 ) -> fmt::Result {
131 write!(f, "\"{self}\"")
132 }
133}
134impl<C: 'static + Ctx> crate::ast::SimpleAttri<C> for PowerGroundBooleanExpression {
135 #[inline]
136 fn nom_parse<'a>(
137 i: &'a str,
138 scope: &mut ParseScope<'_>,
139 ) -> crate::ast::SimpleParseRes<'a, Self::Builder> {
140 <Self::Builder as crate::ast::SimpleAttri<DefaultCtx>>::nom_parse(i, scope)
141 }
142 #[inline]
143 fn fmt_self<T: Write, I: Indentation>(
144 &self,
145 f: &mut CodeFormatter<'_, T, I>,
146 ) -> fmt::Result {
147 write!(f, "\"{self}\"")
148 }
149}
150
151impl Deref for LogicBooleanExpression {
152 type Target = BddBooleanExpression;
153 #[inline]
154 fn deref(&self) -> &Self::Target {
155 &self.0
156 }
157}
158
159impl DerefMut for LogicBooleanExpression {
160 #[inline]
161 fn deref_mut(&mut self) -> &mut Self::Target {
162 &mut self.0
163 }
164}
165
166impl Deref for PowerGroundBooleanExpression {
167 type Target = BddBooleanExpression;
168 #[inline]
169 fn deref(&self) -> &Self::Target {
170 &self.0
171 }
172}
173
174impl DerefMut for PowerGroundBooleanExpression {
175 #[inline]
176 fn deref_mut(&mut self) -> &mut Self::Target {
177 &mut self.0
178 }
179}
180
181#[derive(Debug, Clone)]
207#[derive(serde::Serialize, serde::Deserialize)]
208pub struct BddBooleanExpression {
209 pub expr: Expr,
211 pub bdd: Bdd,
214}
215
216#[derive(Debug, Clone)]
217#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
218#[derive(serde::Serialize, serde::Deserialize)]
219pub struct PowerGroundBooleanExpression(pub BddBooleanExpression);
220
221#[derive(Debug, Clone)]
222#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
223#[derive(serde::Serialize, serde::Deserialize)]
224pub struct LogicBooleanExpression(pub BddBooleanExpression);
225
226impl Borrow<Expr> for BddBooleanExpression {
227 #[inline]
228 fn borrow(&self) -> &Expr {
229 &self.expr
230 }
231}
232impl From<BddBooleanExpression> for Expr {
233 #[inline]
234 fn from(val: BddBooleanExpression) -> Self {
235 val.expr
236 }
237}
238impl PartialEq for BddBooleanExpression {
239 #[inline]
240 fn eq(&self, other: &Self) -> bool {
241 self.bdd == other.bdd
242 }
243}
244
245impl Eq for BddBooleanExpression {}
246#[expect(clippy::non_canonical_partial_ord_impl)]
247impl PartialOrd for BddBooleanExpression {
248 #[inline]
249 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
250 Some(Bdd::cmp_structural(&self.bdd, &other.bdd))
251 }
252}
253impl Ord for BddBooleanExpression {
254 #[inline]
255 fn cmp(&self, other: &Self) -> Ordering {
256 self.partial_cmp(other).unwrap_or(Ordering::Equal)
257 }
258}
259
260impl core::hash::Hash for BddBooleanExpression {
261 #[inline]
262 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
263 self.bdd.hash(state);
264 }
265}
266
267impl LogicBooleanExpression {
268 #[must_use]
269 #[inline]
270 pub fn new(expr: Expr, logic_variables: &BddVariableSet) -> Self {
271 Self(BddBooleanExpression {
272 bdd: logic_variables.safe_eval_expression(&expr).unwrap_or_else(|| {
273 crate::error!("Failed to build BDD for [{expr}]");
274 logic_variables.mk_false()
275 }),
276 expr,
277 })
278 }
279 #[must_use]
280 #[inline]
281 pub fn bdd_new(bdd: Bdd, logic_variables: &BddVariableSet) -> Self {
282 Self(BddBooleanExpression {
283 expr: bdd.to_boolean_expression(logic_variables),
284 bdd,
285 })
286 }
287}
288
289impl<C: 'static + Ctx> ParsingBuilder<C> for LogicBooleanExpression {
290 type Builder = BooleanExpression;
291 #[inline]
292 fn build(builder: Self::Builder, scope: &mut crate::ast::BuilderScope<C>) -> Self {
293 Self::new(builder.expr, &scope.cell_extra_ctx.logic_variables)
294 }
295}
296
297impl PowerGroundBooleanExpression {
298 #[must_use]
299 #[inline]
300 pub fn new(expr: Expr, pg_variables: &BddVariableSet) -> Self {
301 Self(BddBooleanExpression {
302 bdd: pg_variables.safe_eval_expression(&expr).unwrap_or_else(|| {
303 crate::error!("Failed to build BDD for [{expr}]");
304 pg_variables.mk_false()
305 }),
306 expr,
307 })
308 }
309 #[must_use]
310 #[inline]
311 pub fn bdd_new(bdd: Bdd, pg_variables: &BddVariableSet) -> Self {
312 Self(BddBooleanExpression { expr: bdd.to_boolean_expression(pg_variables), bdd })
313 }
314}
315impl<C: 'static + Ctx> ParsingBuilder<C> for PowerGroundBooleanExpression {
316 type Builder = BooleanExpression;
317 #[inline]
318 fn build(builder: Self::Builder, scope: &mut crate::ast::BuilderScope<C>) -> Self {
319 Self::new(builder.expr, &scope.cell_extra_ctx.pg_variables)
320 }
321}
322
323pub struct BooleanExpressionDisplay<'a>(pub &'a Expr);
324impl fmt::Display for BooleanExpressionDisplay<'_> {
325 #[inline]
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 parser::_fmt(self.0, f)
328 }
329}
330impl fmt::Debug for BooleanExpressionDisplay<'_> {
331 #[inline]
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 f.debug_tuple("BoolExpr").field(&format!("{self}")).finish()
334 }
335}
336
337impl fmt::Display for BooleanExpression {
338 #[inline]
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 write!(f, "{}", BooleanExpressionDisplay(&self.expr))
341 }
342}
343
344impl fmt::Display for LogicBooleanExpression {
345 #[inline]
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 write!(f, "{}", BooleanExpressionDisplay(&self.expr))
348 }
349}
350
351impl fmt::Display for PowerGroundBooleanExpression {
352 #[inline]
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 write!(f, "{}", BooleanExpressionDisplay(&self.expr))
355 }
356}
357
358impl<C: 'static + Ctx> crate::Cell<C> {
359 #[inline]
360 pub fn build_logic_boolexpr(
361 &self,
362 expr: Expr,
363 ) -> Result<LogicBooleanExpression, BoolExprErr> {
364 self
365 .extra_ctx
366 .logic_variables()
367 .safe_eval_expression(&expr)
368 .map_or(Err(BoolExprErr::FailToBuildBdd), |bdd| {
369 Ok(LogicBooleanExpression(BddBooleanExpression { expr, bdd }))
370 })
371 }
372 #[inline]
373 pub fn parse_logic_boolexpr(
374 &self,
375 s: &str,
376 ) -> Result<LogicBooleanExpression, BoolExprErr> {
377 let expr = BooleanExpression::from_str(s)?.expr;
378 self.build_logic_boolexpr(expr)
379 }
380 #[inline]
381 pub fn build_pg_boolexpr(
382 &self,
383 expr: Expr,
384 ) -> Result<PowerGroundBooleanExpression, BoolExprErr> {
385 self
386 .extra_ctx
387 .pg_variables()
388 .safe_eval_expression(&expr)
389 .map_or(Err(BoolExprErr::FailToBuildBdd), |bdd| {
390 Ok(PowerGroundBooleanExpression(BddBooleanExpression { expr, bdd }))
391 })
392 }
393 #[inline]
394 pub fn parse_pg_boolexpr(
395 &self,
396 s: &str,
397 ) -> Result<PowerGroundBooleanExpression, BoolExprErr> {
398 let expr = BooleanExpression::from_str(s)?.expr;
399 self.build_pg_boolexpr(expr)
400 }
401}
402
403#[inline]
404fn _get_nodes<'a>(
405 expr: &'a Expr,
406 node_set: &mut HashSet<&'a str, crate::ast::RandomState>,
407) {
408 match expr {
409 Expr::Const(_) => (),
410 Expr::Variable(node) => {
411 _ = node_set.insert(node);
412 }
413 Expr::Not(e) => _get_nodes(e, node_set),
414 Expr::And(e1, e2) | Expr::Or(e1, e2) | Expr::Xor(e1, e2) => {
415 _get_nodes(e1, node_set);
416 _get_nodes(e2, node_set);
417 }
418 Expr::Cond(e1, e2, e3) => {
419 _get_nodes(e1, node_set);
420 _get_nodes(e2, node_set);
421 _get_nodes(e3, node_set);
422 }
423 Expr::Imp(_, _) | Expr::Iff(_, _) => unreachable!(),
424 }
425}
426
427#[inline]
428fn _previous(expr: &mut Expr) {
429 match expr {
430 Expr::Const(_) => (),
431 Expr::Variable(node) => {
432 *node += "~";
433 }
434 Expr::Not(e) => _previous(e),
435 Expr::And(e1, e2) | Expr::Or(e1, e2) | Expr::Xor(e1, e2) => {
436 _previous(e1);
437 _previous(e2);
438 }
439 Expr::Imp(_, _) | Expr::Iff(_, _) | Expr::Cond(_, _, _) => unreachable!(),
440 }
441}
442
443#[cfg(test)]
444mod test {
445 use super::*;
446 use crate::DefaultCtx;
447 use core::{f64::consts::E, str::FromStr as _};
448 use itertools::Itertools as _;
449 impl From<BooleanExpression> for BddBooleanExpression {
450 #[inline]
451 fn from(value: BooleanExpression) -> Self {
452 let node_set = value.expr.support_set();
453 let mut node_set: Vec<&str> = node_set.iter().map(String::as_str).collect();
454 node_set.sort_unstable();
455 let variables = BddVariableSet::new(&node_set);
456 Self {
457 bdd: variables.eval_expression(&value.expr),
458 expr: value.expr,
459 }
460 }
461 }
462 impl core::str::FromStr for BddBooleanExpression {
463 type Err = BoolExprErr;
464 #[inline]
465 fn from_str(s: &str) -> Result<Self, Self::Err> {
466 let expr = BooleanExpression::from_str(s)?;
467 Ok(expr.into())
468 }
469 }
470
471 #[test]
472 fn parse_fmt_self_check() {
473 for (should_success, s) in [
474 (true, "A"),
475 (true, "A^B+C"),
476 (true, "(A+B)*(C+D)"),
477 (true, r#"\"1A\" + \"1B\""#),
478 (true, r#"A_1 + \"1B_2\""#),
479 (true, "(A+B)*(C)"),
480 (true, "!(A+((C+A^!!!B))')"),
481 (true, "!(A&B)"),
482 (true, "!(1&B)"),
483 (true, "A+B+C+D"),
484 (true, "B0’ + C"),
485 (true, "A+B+C+D"),
486 (true, "A+(B+C)^D"),
487 (true, "!(1A&B)"),
488 (true, "!(A B)"),
489 (true, "!(A+B')"),
490 (true, "!(A+B')|C"),
491 (true, "(A)'''"),
492 (true, "!!!(((A)))''"),
493 (true, "!!(!((A))')'"),
494 (false, ""),
495 (false, "!"),
496 (false, "A)"),
497 (true, "1A"),
498 (false, "2A"),
499 (false, "(A"),
500 ] {
501 println!("----");
502 println!("origin: {s}");
503 let bool_expr = BddBooleanExpression::from_str(s);
504 if should_success {
505 if let Ok(e) = bool_expr {
506 let fmt_s = format!("{}", BooleanExpression { expr: e.clone().expr });
507 println!("parsed: {fmt_s}");
508 let fmt_bool_expr = BddBooleanExpression::from_str(&fmt_s);
509 if let Ok(fmt_e) = fmt_bool_expr {
510 println!("reparsed: {}", BooleanExpression { expr: fmt_e.clone().expr });
511 assert_eq!(e, fmt_e);
512 } else {
513 println!("{e:?}");
514 println!("{fmt_bool_expr:?}");
515 panic!("not equal");
516 }
517 } else {
518 println!("{bool_expr:?}");
519 panic!("It should success");
520 }
521 } else if let Err(e) = bool_expr {
522 println!("{e}");
523 } else {
524 panic!("It should go wrong");
525 }
526 }
527 }
528 #[test]
529 fn parse_hash() {
530 for (same, s1, s2) in [
531 (true, "!!(!((A))')'", "!A"),
532 (true, "A*C+B*C", "(A+B)*C"),
533 (true, "B*D+B*C+A*D+A*C", "(A+B)*(C+D)"),
534 (false, "A+B^C", "A^B+C"),
535 (true, "(A+B)+C", "A+(B+C)"),
536 (true, "1A", "1"),
539 (true, "1A+B", "1+B"),
540 ] {
541 println!("----");
542 println!("s1: {s1}");
543 println!("s2: {s2}");
544 if same {
545 println!("they should same");
546 assert_eq!(
547 BddBooleanExpression::from_str(s1),
548 BddBooleanExpression::from_str(s2)
549 );
550 } else {
551 println!("they are different");
552 assert_ne!(
553 BddBooleanExpression::from_str(s1),
554 BddBooleanExpression::from_str(s2)
555 );
556 }
557 }
558 }
559 #[test]
561 fn if_else() {
562 let a = Box::new(Expr::Variable("A".to_owned()));
563 let b = Box::new(Expr::Variable("B".to_owned()));
564 let c = Box::new(Expr::Variable("C".to_owned()));
565 let cond: BddBooleanExpression =
566 BooleanExpression { expr: Expr::Cond(a.clone(), b.clone(), c.clone()) }.into();
567 let or_and: BddBooleanExpression = BooleanExpression {
568 expr: Expr::Or(
569 Box::new(Expr::And(a.clone(), b)),
570 Box::new(Expr::And(Box::new(Expr::Not(a)), c)),
571 ),
572 }
573 .into();
574 assert_eq!(cond, or_and);
575 }
576 #[test]
577 fn lid_bdd() {
578 let variables = BddVariableSet::new(&["A", "B", "C", "D"]);
579 let x1 = variables.eval_expression_string("(A|B)&(C|D)");
580 let x2 = variables.eval_expression_string("B&D | B&C | A&D | A&C");
581 assert_eq!(x1, x2);
582 println!("{variables}");
583 for valuation in x1.sat_valuations() {
584 println!("{valuation}");
585 assert!(x1.eval_in(&valuation));
586 }
587 }
588 #[test]
589 fn lid_bdd2() {
590 let variables = BddVariableSet::new(&["A", "B", "C", "D"]);
591 let x1 = variables.eval_expression_string("(A|B)&(C|D)");
592 let variables = BddVariableSet::new(&["A", "B", "C", "D", "E"]);
593 let x2 = variables.eval_expression_string("(A|B)&(C|D)");
594 assert_ne!(x1, x2);
595 }
596}