Skip to content

Commit

Permalink
add: basic graphviz support
Browse files Browse the repository at this point in the history
  • Loading branch information
jsfpdn committed Apr 2, 2024
1 parent 1657428 commit f616a4e
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 84 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ Cargo.lock
# Added by cargo

/target

# Generated .dot files for Graphviz
*.dot
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ repos:
name: cargo test
description: Run unit tests
language: rust
entry: cargo test
entry: cargo test --workspace
files: \.rs$
pass_filenames: false
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ description = "Bottom-up sentential decision diagram compiler binary."
[workspace]

[dependencies]
clap = { version = "4.5.4", features = ["derive"] }
sdd-rs-lib = { path = "sdd-rs-lib" }
3 changes: 3 additions & 0 deletions sdd-rs-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ description = "Bottom-up sentential decision diagram compiler library."
[lib]
name = "sddrs"
path = "lib.rs"

[dependencies]
fxhash = "0.2.1"
87 changes: 87 additions & 0 deletions sdd-rs-lib/dot_writer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use crate::manager::Result;

pub trait Dot {
fn draw(&self, writer: &mut DotWriter);
}

#[derive(Default)]
pub struct DotWriter {
nodes: Vec<(usize, NodeType)>,
edges: Vec<((usize, Option<usize>), usize)>,
}

pub enum NodeType {
Box(String),
Circle(u32),
Record(String, String),
}

impl NodeType {
fn shape(&self) -> String {
let shape_type = match self {
NodeType::Box(_) => "box",
NodeType::Record(_, _) => "record",
NodeType::Circle(_) => "circle",
}
.to_owned();

format!("shape={shape_type}")
}

fn label(&self) -> String {
match self {
NodeType::Record(fst, snd) => format!("label=\"<f0> {fst} | <f1> {snd}\""),
NodeType::Circle(label) => format!("label=\"{label}\""),
NodeType::Box(_) => String::new(),
}
}

fn metadata() -> String {
"height=.25 width=.2".to_owned()
}
}

impl DotWriter {
#[must_use]
pub fn new() -> DotWriter {
DotWriter::default()
}

pub fn add_node(&mut self, node_idx: usize, node_type: NodeType) {
self.nodes.push((node_idx, node_type));
}

pub fn add_edge(&mut self, from: (usize, Option<usize>), to: usize) {
self.edges.push((from, to));
}

/// # Errors
/// Function returns an error if the writing to a file or flushing fails.
pub fn write(&self, writer: &mut dyn std::io::Write) -> Result<()> {
write!(writer, "digraph sdd {{\n overlap=false")?;

for (node, node_type) in &self.nodes {
write!(
writer,
"\n {} [{} {} {}]",
node,
node_type.shape(),
node_type.label(),
NodeType::metadata(),
)?;
}

for ((from, from_child), to) in &self.edges {
if let Some(from_child) = from_child {
// TODO: Make the edge begin in the middle of the child.
write!(writer, "\n {from}:f{from_child} -> {to} [arrowsize=.50]")?;
} else {
write!(writer, "\n {from} -> {to} [arrowsize=.50]")?;
}
}

write!(writer, "\n}}")?;
writer.flush()?;
Ok(())
}
}
1 change: 1 addition & 0 deletions sdd-rs-lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pub mod options;
pub mod sdd;
#[macro_use]
pub mod util;
pub mod dot_writer;
pub mod vtree;
51 changes: 41 additions & 10 deletions sdd-rs-lib/literal.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone, PartialOrd, Ord)]
pub struct VarLabel(u64);
use std::fmt::Display;

#[derive(Hash, Eq, PartialEq, Debug, Clone, PartialOrd, Ord)]
// TODO: Do we want String labels?
pub struct VarLabel(String);

impl VarLabel {
#[must_use]
pub fn new(v: u64) -> VarLabel {
VarLabel(v)
pub fn new(v: &str) -> VarLabel {
VarLabel(v.to_owned())
}
}

Expand All @@ -27,15 +30,32 @@ impl Default for VarLabelManager {
}

// Either true or false
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy, PartialOrd, Ord)]
#[derive(Hash, Eq, PartialEq, Debug, Clone, PartialOrd, Ord)]
pub struct Literal {
var_label: VarLabel,
polarity: bool,
polarity: Polarity,
}

#[derive(Hash, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Copy)]
pub enum Polarity {
Positive,
Negative,
}

impl std::ops::Not for Polarity {
type Output = Self;

fn not(self) -> Self::Output {
match self {
Polarity::Positive => Polarity::Negative,
Polarity::Negative => Polarity::Positive,
}
}
}

impl Literal {
#[must_use]
pub fn new(polarity: bool, var_label: VarLabel) -> Literal {
pub fn new(polarity: Polarity, var_label: VarLabel) -> Literal {
Literal {
var_label,
polarity,
Expand All @@ -45,18 +65,18 @@ impl Literal {
#[must_use]
pub fn negate(&self) -> Literal {
Literal {
var_label: VarLabel::new(self.var_label.0),
var_label: VarLabel::new(&self.var_label.0),
polarity: !self.polarity,
}
}

#[must_use]
pub fn eq_negated(&self, other: &Literal) -> bool {
self.var_label() == other.var_label() && self.polarity() != other.polarity()
self.var_label == other.var_label && self.polarity != other.polarity
}

#[must_use]
pub fn polarity(self) -> bool {
pub fn polarity(self) -> Polarity {
self.polarity
}

Expand All @@ -65,3 +85,14 @@ impl Literal {
self.var_label
}
}

impl Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let polarity = if self.polarity == Polarity::Positive {
""
} else {
"!"
};
write!(f, "{}{}", polarity, self.var_label.0)
}
}
35 changes: 31 additions & 4 deletions sdd-rs-lib/manager.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::collections::HashMap;

use crate::dot_writer::{Dot, DotWriter};
use crate::literal::VarLabelManager;
use crate::options::SddOptions;
use crate::sdd::Node;
use crate::sdd::{Node, Sdd};
use crate::vtree::VTreeManager;

pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[allow(clippy::module_name_repetitions)]
pub struct SddManager<'a> {
// TODO: Remove all #[allow(unused)] directives.
Expand All @@ -19,7 +22,7 @@ pub struct SddManager<'a> {

// Unique table holding all the decision nodes.
// More details can be found in [Algorithms and Data Structures in VLSI Design](https://link.springer.com/book/10.1007/978-3-642-58940-9).
unqiue_table: HashMap<u64, Node<'a>>,
unqiue_table: HashMap<u64, Sdd<'a>>,
// u64 is the hash of sdd::Decision
// TODO: Should we store sdd::Decision or sdd::Node?
}
Expand All @@ -35,8 +38,10 @@ impl<'a> SddManager<'a> {
}
}

// TODO: This function should be removed as user should not be able to fill the unique_table
// directly.
#[must_use]
pub fn new_with_nodes(options: SddOptions, nodes: HashMap<u64, Node<'a>>) -> SddManager {
pub fn new_with_nodes(options: SddOptions, nodes: HashMap<u64, Sdd<'a>>) -> SddManager {
SddManager {
options,
vtree_manager: VTreeManager::new(),
Expand All @@ -46,7 +51,7 @@ impl<'a> SddManager<'a> {
}

#[must_use]
pub fn get_node(&self, id: &u64) -> Option<&'a Node> {
pub fn get_node(&self, id: &u64) -> Option<&'a Sdd> {
self.unqiue_table.get(id)
}

Expand All @@ -60,5 +65,27 @@ impl<'a> SddManager<'a> {
pub fn exist() {}
pub fn forall() {}

/// # Errors
/// Returns an error if TBD.
pub fn draw_sdd_graph(&self, writer: &mut dyn std::io::Write) -> Result<()> {
let mut dot_writer = DotWriter::new();
for node in self.unqiue_table.values() {
node.draw(&mut dot_writer);
}
dot_writer.write(writer)
}

/// # Errors
/// Returns an error if TBD.
pub fn draw_vtree_graph(&self, writer: &mut dyn std::io::Write) -> Result<()> {
// TODO: Delete the function body and implement draw for vtree.
let mut dot_writer = DotWriter::new();
for node in self.unqiue_table.values() {
node.draw(&mut dot_writer);
}
dot_writer.write(writer)?;

unimplemented!("TBD")
}
// TODO: expose operations manipulating the vtree.
}
Loading

0 comments on commit f616a4e

Please sign in to comment.