Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Max Willsey <me@mwillsey.com>"]
categories = ["data-structures"]
description = "An implementation of egraphs"
edition = "2018"
edition = "2021"
keywords = ["e-graphs"]
license = "MIT"
name = "egg"
Expand Down
36 changes: 18 additions & 18 deletions src/dot.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*!
EGraph visualization with [GraphViz]

Use the [`Dot`] struct to visualize an [`EGraph`]
Use the [`Dot`] struct to visualize an [`EGraph`](crate::EGraph)

[GraphViz]: https://graphviz.gitlab.io/
!*/
Expand All @@ -11,13 +11,13 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::io::{Error, ErrorKind, Result, Write};
use std::path::Path;

use crate::{egraph::EGraph, Analysis, Language};
use crate::{raw::EGraphResidual, Language};

/**
A wrapper for an [`EGraph`] that can output [GraphViz] for
A wrapper for an [`EGraphResidual`] that can output [GraphViz] for
visualization.

The [`EGraph::dot`](EGraph::dot()) method creates `Dot`s.
The [`EGraphResidual::dot`] method creates `Dot`s.

# Example

Expand Down Expand Up @@ -50,19 +50,18 @@ instead of to its own eclass.

[GraphViz]: https://graphviz.gitlab.io/
**/
pub struct Dot<'a, L: Language, N: Analysis<L>> {
pub(crate) egraph: &'a EGraph<L, N>,
pub struct Dot<'a, L: Language> {
pub(crate) egraph: &'a EGraphResidual<L>,
/// A list of strings to be output top part of the dot file.
pub config: Vec<String>,
/// Whether or not to anchor the edges in the output.
/// True by default.
pub use_anchors: bool,
}

impl<'a, L, N> Dot<'a, L, N>
impl<'a, L> Dot<'a, L>
where
L: Language + Display,
N: Analysis<L>,
{
/// Writes the `Dot` to a .dot file with the given filename.
/// Does _not_ require a `dot` binary.
Expand Down Expand Up @@ -170,16 +169,15 @@ where
}
}

impl<'a, L: Language, N: Analysis<L>> Debug for Dot<'a, L, N> {
impl<'a, L: Language> Debug for Dot<'a, L> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("Dot").field(self.egraph).finish()
}
}

impl<'a, L, N> Display for Dot<'a, L, N>
impl<'a, L> Display for Dot<'a, L>
where
L: Language + Display,
N: Analysis<L>,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
writeln!(f, "digraph egraph {{")?;
Expand All @@ -192,37 +190,39 @@ where
writeln!(f, " {}", line)?;
}

let classes = self.egraph.generate_class_nodes();

// define all the nodes, clustered by eclass
for class in self.egraph.classes() {
writeln!(f, " subgraph cluster_{} {{", class.id)?;
for (&id, class) in &classes {
writeln!(f, " subgraph cluster_{} {{", id)?;
writeln!(f, " style=dotted")?;
for (i, node) in class.iter().enumerate() {
writeln!(f, " {}.{}[label = \"{}\"]", class.id, i, node)?;
writeln!(f, " {}.{}[label = \"{}\"]", id, i, node)?;
}
writeln!(f, " }}")?;
}

for class in self.egraph.classes() {
for (&id, class) in &classes {
for (i_in_class, node) in class.iter().enumerate() {
let mut arg_i = 0;
node.try_for_each(|child| {
// write the edge to the child, but clip it to the eclass with lhead
let (anchor, label) = self.edge(arg_i, node.len());
let child_leader = self.egraph.find(child);

if child_leader == class.id {
if child_leader == id {
writeln!(
f,
// {}.0 to pick an arbitrary node in the cluster
" {}.{}{} -> {}.{}:n [lhead = cluster_{}, {}]",
class.id, i_in_class, anchor, class.id, i_in_class, class.id, label
id, i_in_class, anchor, id, i_in_class, id, label
)?;
} else {
writeln!(
f,
// {}.0 to pick an arbitrary node in the cluster
" {}.{}{} -> {}.0 [lhead = cluster_{}, {}]",
class.id, i_in_class, anchor, child, child_leader, label
id, i_in_class, anchor, child, child_leader, label
)?;
}
arg_i += 1;
Expand Down
28 changes: 15 additions & 13 deletions src/eclass.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
use std::fmt::Debug;
use std::fmt::{Debug, Formatter};
use std::iter::ExactSizeIterator;

use crate::*;

/// An equivalence class of enodes.
/// The additional data required to turn a [`raw::RawEClass`] into a [`EClass`]
#[non_exhaustive]
#[derive(Debug, Clone)]
#[derive(Clone)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct EClass<L, D> {
/// This eclass's id.
pub id: Id,
pub struct EClassData<L, D> {
/// The equivalent enodes in this equivalence class.
pub nodes: Vec<L>,
/// The analysis data associated with this eclass.
///
/// Modifying this field will _not_ cause changes to propagate through the e-graph.
/// Prefer [`EGraph::set_analysis_data`] instead.
pub data: D,
/// The parent enodes and their original Ids.
pub(crate) parents: Vec<(L, Id)>,
}

impl<L: Language, D: Debug> Debug for EClassData<L, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut nodes = self.nodes.clone();
nodes.sort();
write!(f, "({:?}): {:?}", self.data, nodes)
}
}

/// An equivalence class of enodes
pub type EClass<L, D> = raw::RawEClass<EClassData<L, D>>;

impl<L, D> EClass<L, D> {
/// Returns `true` if the `eclass` is empty.
pub fn is_empty(&self) -> bool {
Expand All @@ -36,11 +43,6 @@ impl<L, D> EClass<L, D> {
pub fn iter(&self) -> impl ExactSizeIterator<Item = &L> {
self.nodes.iter()
}

/// Iterates over the parent enodes of this eclass.
pub fn parents(&self) -> impl ExactSizeIterator<Item = (&L, Id)> {
self.parents.iter().map(|(node, id)| (node, *id))
}
}

impl<L: Language, D> EClass<L, D> {
Expand Down
Loading