Skip to content

Commit

Permalink
refac: improve documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
jsfpdn committed Dec 15, 2024
1 parent 81f63e2 commit a61801a
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 23 deletions.
118 changes: 116 additions & 2 deletions sddrs/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,121 @@
//! # Bottom-up compiler for Sentential Decision Diagrams.
//!
//! Incrementally build, manipualate, and optimize
//! [Sentential Decision Diagrams (SDD)](https://en.wikipedia.org/wiki/Sentential_decision_diagram):
//! a succinct representation of Boolean functions.
//!
//! The compiler currently supports:
//! * incremental compilation of Boolean functions (knowledge bases) to *compressed* and *trimmed* SDDs,
//! * efficient querying of model count, model enumeration, and equivalence of SDDs,
//! * dynamic minimization of SDDs via *vtree fragments*,
//! * garbage collection of dead nodes,
//! * SDD compilation from CNF in
//! [DIMACS](https://www21.in.tum.de/~lammich/2015_SS_Seminar_SAT/resources/dimacs-cnf.pdf) format.
//!
//!
//!
//!
//!
//!
//! The following snippet compiles the function `(A ∧ B) ∨ C` to SDD,
//! computes number of its models, enumerates and prints them to the stdout,
//! and renders the compiled SDD and its vtree to the DOT format.
//!
//! ```rust
//! use sddrs::manager::{options, GCStatistics, SddManager};
//! use sddrs::literal::literal::Polarity;
//! use bon::arr;
//!
//! fn main() {
//! let options = options::SddOptions::builder()
//! // Create right-linear vtree.
//! .vtree_strategy(options::VTreeStragey::RightLinear)
//! // Initialize the manager with variables A, B, and C.
//! .variables(["A".to_string(), "B".to_string(), "C".to_string()])
//! .build();
//! let manager = SddManager::new(options);
//!
//! // Retrieve SDDs for literals A, B, and C.
//! let a = manager.literal("A", Polarity::Positive).unwrap();
//! let b = manager.literal("B", Polarity::Positive).unwrap();
//! let c = manager.literal("C", Polarity::Positive).unwrap();
//!
//! // Compute "A ∧ B"
//! let a_and_b = manager.conjoin(&a, &b);
//! // Compute "(A ∧ B) ∨ C"
//! let a_and_b_or_c = manager.disjoin(&a_and_b, &c);
//!
//! let model_count = manager.model_count(&a_and_b_or_c);
//! let models = manager.model_enumeration(&a_and_b_or_c);
//!
//! println!("'(A ∧ B) ∨ C' has {model_count} models.");
//! println!("They are:\n{models}");
//!
//! let sdd_path = "my_formula.dot"
//! let f = File::create(sdd_path).unwrap();
//! let mut b = BufWriter::new(f);
//! manager
//! .draw_sdd(&mut b as &mut dyn std::io::Write, &sdd)
//! .unwrap();
//!
//! let vtree_path = "my_vtree.dot"
//! let f = File::create(vtree_path).unwrap();
//! let mut b = BufWriter::new(f);
//! manager
//! .draw_vtree(&mut b as &mut dyn std::io::Write)
//! .unwrap();
//!
//! println!("Rendered SDD to '{sdd_path}' and vtree to '{vtree_path}'");
//! }
//! ```
//!
//! ---
//!
//! Main methods to compile SDDs are:
//!
//! * [`crate::manager::SddManager::conjoin`] -- compute AND of two SDDs
//! * [`crate::manager::SddManager::disjoin`] -- compute OR of two SDDs
//! * [`crate::manager::SddManager::imply`] -- compute implication of two SDDs
//! * [`crate::manager::SddManager::equiv`] -- compute equivalence of two SDDs
//! * [`crate::manager::SddManager::negate`] -- compute negation of SDD
//!
//! Main methods to query SDDs are:
//!
//! * [`crate::manager::SddManager::model_count`] -- count the number of models of an SDD
//! * [`crate::manager::SddManager::model_enumeration`] -- enumerate models of an SDD
//! * [`crate::sdd::SddRef::size`] -- get size of the SDD
//!
//! SDDs can be also minimized after compilation by appropriately manipulating the vtree:
//!
//! * [`crate::manager::SddManager::swap`] -- swap children of a vtree and adjust SDDs
//! * [`crate::manager::SddManager::rotate_right`] -- rotate vtree node to the right and adjust SDDs
//! * [`crate::manager::SddManager::rotate_left`] -- rotate vtree node to the left and adjust SDDs
//!
//! These transformations do not guarantee that the SDD will decrease in size. In fact, it may grow.
//! For this purpose, dynamic minimization via [`crate::vtree::fragment::Fragment`] tries to find
//! the vtree that actually minimizes the SDD the most [`crate::manager::SddManager::minimize`].
//!
//! There are also additional helper functions:
//!
//! * [`crate::manager::SddManager::from_dimacs`] -- compile SDD based on a DIMACS CNF file
//! * [`crate::manager::SddManager::draw_sdd`] -- draw a particular SDD to a DOT Graphviz format
//! * [`crate::manager::SddManager::draw_all_sdds`] -- draw every SDDs to a DOT Graphviz format
//! * [`crate::manager::SddManager::draw_vtree`] -- draw vtree a DOT Graphviz format
//!
//!
//! Additional resources:
//!
//! * [SDD: A New Canonical Representation of Propositional Knowledge Bases - Adnad Darwiche](http://reasoning.cs.ucla.edu/fetch.php?id=121&type=pdf):
//! paper introducing SDDs
//! * [Dynamic Minimization of Sentential Decision Diagrams - Arthur Choi and Adnan Darwiche](http://reasoning.cs.ucla.edu/fetch.php?id=128&type=pdf):
//! paper describing dynamic minimization of SDDs
//! * [SDD: A New Canonical Representation of Propositional Knowledge Bases – Adnan Darwiche](https://www.youtube.com/watch?v=_5Estmve91o): YouTube tutorial
/// Variables, polarities, and literals.
pub mod literal;
pub mod manager;
pub mod sdd;
#[macro_use]
pub mod util;
pub mod dot_writer;
pub(crate) mod util;
pub(crate) mod dot_writer;
pub mod vtree;
3 changes: 2 additions & 1 deletion sddrs/literal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// Variables, polarities, and literals.
pub mod literal;
pub mod manager;
pub(crate) mod manager;

pub(crate) use crate::literal::literal::*;
pub(crate) use crate::literal::manager::*;
1 change: 1 addition & 0 deletions sddrs/manager/dimacs.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! DIMACS module responsible for parsing DIMACS CNF problem files.
use crate::literal::{Polarity, VariableIdx};
use crate::manager::SddManager;
use crate::sdd::SddRef;
Expand Down
26 changes: 12 additions & 14 deletions sddrs/manager/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,11 @@ impl GCStatistics {
}
}

/// [`SddManager`] is a structure responsible for maintaing the state of the compilation.
/// Structure responsible for maintaing the state of the compilation.
/// It is the central piece when compiling Boolean functions --- it is responsible
/// combining SDDs, querying the knowledge, caching operations, collecting garbage
/// (if configured), dynamically minimizing compiled knowledge (if configured),
/// and much more.
///
/// See [`SddOptions`] on how [`SddManager`] can be configured.
/// and much more. See [`SddOptions`] on how [`SddManager`] can be configured.
///
/// ```
/// use sddrs::literal::literal::Polarity;
Expand Down Expand Up @@ -345,7 +343,7 @@ impl SddManager {
}
}

/// Retrieve the SDD representing [`Literal`] with given label and [`Polarity`].
/// Retrieve the SDD representing a literal with given label and [`Polarity`].
/// Returns [`None`] if such variable does not exist.
pub fn literal(&self, label: &str, polarity: Polarity) -> Option<SddRef> {
let (_, variants) = self.literal_manager.borrow().find_by_label(label)?;
Expand Down Expand Up @@ -1378,8 +1376,8 @@ impl SddManager {
self.vtree_manager.borrow().root()
}

/// Rotate the vtree [`x`] to the left and adjust SDDs accordingly.
/// The user must make sure that [`x`] is 'rotatable', i.e., [`x`]
/// Rotate the vtree `x` to the left and adjust SDDs accordingly.
/// The user must make sure that `x` is 'rotatable', i.e., `x`
/// is an internal node and has a parent.
///
/// ```text
Expand All @@ -1391,7 +1389,7 @@ impl SddManager {
/// ```
///
/// This is a low-level operation working directly on a vtree. See
/// [`SddManager::minimization`] for a more sophisticated way of finding better vtrees.
/// [`SddManager::minimize`] for a more sophisticated way of finding better vtrees.
///
/// Children hanged at `w` must be split accordingly, depending on the vtrees
/// they are normalized for:
Expand Down Expand Up @@ -1444,8 +1442,8 @@ impl SddManager {
}
}

/// Rotate the vtree [`x`] to the right and adjust SDDs accordingly.
/// The user must make sure that [`x`] is 'rotatable', i.e., [`x`]
/// Rotate the vtree `x` to the right and adjust SDDs accordingly.
/// The user must make sure that `x` is 'rotatable', i.e., `x`
/// is an internal node an its left child `w` is an internal node as well.
///
/// ```text
Expand All @@ -1457,7 +1455,7 @@ impl SddManager {
/// ```
///
/// This is a low-level operation working directly on a vtree. See
/// [`SddManager::minimization`] for a more sophisticated way of finding better vtrees.
/// [`SddManager::minimize`] for a more sophisticated way of finding better vtrees.
///
/// Children hanged at `w` must be split accordingly, depending on the vtrees
/// they are normalized for:
Expand Down Expand Up @@ -1494,8 +1492,8 @@ impl SddManager {
Ok(())
}

/// Swap children of the given vtree [`x`] and adjust SDDs accordingly.
/// The user must make sure that [`x`] is 'swappable', i.e., it is
/// Swap children of the given vtree `x` and adjust SDDs accordingly.
/// The user must make sure that `x` is 'swappable', i.e., it is
/// an internal node.
///
/// ```text
Expand All @@ -1505,7 +1503,7 @@ impl SddManager {
/// ```
///
/// This is a low-level operation working directly on a vtree. See
/// [`SddManager::minimization`] for a more sophisticated way of finding better vtrees.
/// [`SddManager::minimize`] for a more sophisticated way of finding better vtrees.
///
/// # Errors
///
Expand Down
2 changes: 2 additions & 0 deletions sddrs/manager/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Manager responsible for manipulating and querying SDDs as well as maintaining
//! the state of the compilation.
mod manager;

pub mod dimacs;
Expand Down
1 change: 1 addition & 0 deletions sddrs/manager/model.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Models of the compiled knowledge base.
use crate::literal::{Literal, Polarity, Variable};
use bitvec::prelude::*;
use std::fmt::Display;
Expand Down
3 changes: 2 additions & 1 deletion sddrs/manager/options.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Configure [`crate::manager::SddManager`].
use bon::Builder;
use clap::ValueEnum;

Expand Down Expand Up @@ -58,7 +59,7 @@ pub enum MinimizationCutoff {
BreakAfterFirstImprovement,
}

/// [`sddrs::manager::SddManager`] configuration options. See individual
/// [`crate::manager::SddManager`] configuration options. See individual
/// fields for more information.
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Builder)]
Expand Down
1 change: 1 addition & 0 deletions sddrs/sdd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Sentential Decision Diagrams.
mod decision;
mod element;
mod sdd;
Expand Down
5 changes: 2 additions & 3 deletions sddrs/sdd/sdd_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ impl SddRef {
return negation == other.id();
}

// TODO: This may cause panic w.r.t. borrowing here and later when negating.
let fst_sdd_type = self.0.borrow().sdd_type.clone();
let snd_sdd_type = other.0.borrow().sdd_type.clone();

Expand Down Expand Up @@ -189,7 +188,7 @@ impl SddRef {
self.0.borrow().canonicalize(manager)
}

/// Recursively check whether [`self`] and all its descendants are trimmed.
/// Recursively check whether [`SddRef`] and all its descendants are trimmed.
/// SDD is trimmed if it does not contain decompositions in the form of
/// `{(true, alpha)}` and `{(alpha, true), (!alpha, false)}`.
pub fn is_trimmed(&self, manager: &SddManager) -> bool {
Expand All @@ -198,7 +197,7 @@ impl SddRef {

/// Recursivelly checks whether the SDD is compressed.
/// Decision node is compressed if all subs are distinct, i.e.,
/// for all indexes i,j such that i != j, it holds that `s_i != s_j`.
/// for all indexes i,j such that i != j, it holds that s_i != s_j.
///
/// See definition 8 in [SDD: A New Canonical Representation of Propositional Knowledge Bases](https://ai.dmi.unibas.ch/research/reading_group/darwiche-ijcai2011.pdf).
///
Expand Down
4 changes: 4 additions & 0 deletions sddrs/vtree/fragment.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Fragment of a [`crate::vtree::VTreeRef`] used for dynamic minimization
//! [`crate::manager::SddManager::minimize`] to decrease the size of [`crate::sdd::SddRef`].
#![allow(clippy::many_single_char_names, clippy::similar_names)]

use anyhow::{Context, Result};
Expand Down Expand Up @@ -61,6 +63,8 @@ const MOVES_RIGHT_LINEAR: [Move; 12] = [
Move::SwapChild,
];

/// Fragment represents a part of a vtree. It is defined by a root
/// and a child. It can be either left-linear or right-linear.
#[derive(Debug, Clone)]
pub struct Fragment {
current_root: VTreeRef,
Expand Down
3 changes: 2 additions & 1 deletion sddrs/vtree/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod fragment;
//! Vtrees and fragments.
pub mod fragment;
mod vtree;

pub(crate) use crate::vtree::fragment::*;
Expand Down
2 changes: 1 addition & 1 deletion sddrs/vtree/vtree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ pub(crate) enum VTreeOrder {
RightSubOfLeft,
}

/// Vtree is a node of a full binary tree.
/// Vtree is a node of a full binary tree for which an [`crate::sdd::SddRef`] is normalized.
#[derive(Debug, Clone)]
pub struct VTreeRef(pub(crate) Rc<RefCell<VTree>>);

Expand Down

0 comments on commit a61801a

Please sign in to comment.