liberty_db/expression/boolean_expression/
mod.rs

1#![allow(clippy::unnecessary_box_returns, clippy::used_underscore_items)]
2//! <script>
3//! IFRAME('https://zao111222333.github.io/liberty-db/2020.09/reference_manual.html');
4//! </script>
5mod 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  /// `A & B` -> `A* & B*`
33  #[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/// <a name ="reference_link" href="
46/// https://zao111222333.github.io/liberty-db/2020.09/reference_manual.html?field=test&bgn=132.36+132.41&end=132.38+133.13
47/// ">Reference</a>
48///
49/// | Operator | Description                           |
50/// | -------- | ------------------------------------- |
51/// | '        | invert previous expression            |
52/// | ’        | invert previous expression(?)         |
53/// | !        | invert following expression           |
54/// | ^        | logical XOR                           |
55/// | \*       | logical AND                           |
56/// | &        | logical AND                           |
57/// | space    | logical AND (when no other separator) |
58/// | \+       | logical OR                            |
59/// | \|       | logical OR                            |
60/// | 1        | signal tied to logic 1                |
61/// | 0        | signal tied to logic 0                |
62///
63/// A pin name beginning with a number must be enclosed in double quotation marks preceded by a backslash (\), as in the following example
64/// ``` liberty
65/// function : " \"1A\" + \"1B\" " ;
66/// ```
67/// <script>
68/// IFRAME('https://zao111222333.github.io/liberty-db/2020.09/reference_manual.html');
69/// </script>
70#[derive(Debug, Clone)]
71#[derive(serde::Serialize, serde::Deserialize)]
72pub struct BooleanExpression {
73  /// `BooleanExpression` itself
74  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/// <a name ="reference_link" href="
182/// https://zao111222333.github.io/liberty-db/2020.09/reference_manual.html?field=test&bgn=132.36+132.41&end=132.38+133.13
183/// ">Reference</a>
184///
185/// | Operator | Description                           |
186/// | -------- | ------------------------------------- |
187/// | '        | invert previous expression            |
188/// | ’        | invert previous expression(?)         |
189/// | !        | invert following expression           |
190/// | ^        | logical XOR                           |
191/// | \*       | logical AND                           |
192/// | &        | logical AND                           |
193/// | space    | logical AND (when no other separator) |
194/// | \+       | logical OR                            |
195/// | \|       | logical OR                            |
196/// | 1        | signal tied to logic 1                |
197/// | 0        | signal tied to logic 0                |
198///
199/// A pin name beginning with a number must be enclosed in double quotation marks preceded by a backslash (\), as in the following example
200/// ``` liberty
201/// function : " \"1A\" + \"1B\" " ;
202/// ```
203/// <script>
204/// IFRAME('https://zao111222333.github.io/liberty-db/2020.09/reference_manual.html');
205/// </script>
206#[derive(Debug, Clone)]
207#[derive(serde::Serialize, serde::Deserialize)]
208pub struct BddBooleanExpression {
209  /// `BooleanExpression` itself
210  pub expr: Expr,
211  /// Use [binary decision diagrams](https://en.wikipedia.org/wiki/Binary_decision_diagram) (BDDs)
212  /// as `id`, to impl `hash` and `compare`
213  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      // skip this case, should guarantee same variables
537      // (false, "A+B+C", "A+B+D"),
538      (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  /// `cond ? then_exp : else_exp` is equal to `(cond & then_exp) | (!cond & else_exp)`
560  #[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}