Skip to content

Commit

Permalink
Optimize constant substitution
Browse files Browse the repository at this point in the history
Rather than serialize ergotree and then parse it back with ConstantStore, constant substitution is now done by doing a tree traversal and rewriting ConstantPlaceholder nodes with Constant
  • Loading branch information
SethDusek committed Oct 21, 2024
1 parent 352eb2a commit 002ec13
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 14 deletions.
16 changes: 2 additions & 14 deletions ergotree-ir/src/ergo_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,23 +211,11 @@ impl ErgoTree {
/// get Expr out of ErgoTree
pub fn proposition(&self) -> Result<Expr, ErgoTreeError> {
let tree = self.parsed_tree()?.clone();
let root = tree.root;
// This tree has ConstantPlaceholder nodes instead of Constant nodes.
// We need to substitute placeholders with constant values.
// So far the easiest way to do it is during deserialization (after the serialization)
let root = tree.root;
if tree.header.is_constant_segregation() {
let mut data = Vec::new();
let constants = {
let cs = ConstantStore::new(tree.constants.clone());
let mut w = SigmaByteWriter::new(&mut data, Some(cs));
root.sigma_serialize(&mut w)?;
#[allow(clippy::unwrap_used)] // constant store is specified in SigmaByteWriter::new
w.constant_store.unwrap()
};
let cursor = Cursor::new(&mut data[..]);
let mut sr = SigmaByteReader::new_with_substitute_placeholders(cursor, constants);
let parsed_expr = Expr::sigma_parse(&mut sr)?;
Ok(parsed_expr)
Ok(root.substitute_constants(&tree.constants)?)
} else {
Ok(root)
}
Expand Down
53 changes: 53 additions & 0 deletions ergotree-ir/src/mir/expr.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! IR expression
use std::convert::Infallible;
use std::convert::TryFrom;
use std::convert::TryInto;

use crate::pretty_printer::PosTrackingWriter;
use crate::pretty_printer::Print;
use crate::serialization::SigmaParsingError;
use crate::source_span::Spanned;
use crate::traversable::Traversable;
use crate::types::stype::LiftIntoSType;
Expand Down Expand Up @@ -326,6 +328,57 @@ impl Expr {
}
}

/// Rewrite tree, matching nodes that satisfy [`rule`] and applying [`subst`] to them
pub fn rewrite_bu(&self, rule: impl Fn(&Expr) -> bool, subst: impl Fn(&mut Expr)) -> Self {
let mut new_root = self.clone();
Self::rewrite_bu_inner(&mut new_root, &rule, &|node| {
Ok::<_, Infallible>(subst(node))
})
.unwrap();
new_root
}

/// Attempt to rewrite tree, returning early if [`subst`] returns `Err(E)`
pub fn try_rewrite_bu<E>(
&self,
rule: impl Fn(&Expr) -> bool,
subst: impl Fn(&mut Expr) -> Result<(), E>,
) -> Result<Self, E> {
let mut new_root = self.clone();
Self::rewrite_bu_inner(&mut new_root, &rule, &subst).map(|_| new_root)
}

fn rewrite_bu_inner<E>(
root: &mut Expr,
rule: &impl Fn(&Expr) -> bool,
subst: &impl Fn(&mut Expr) -> Result<(), E>,
) -> Result<(), E> {
root.children_mut()
.try_for_each(|expr| Self::rewrite_bu_inner(expr, rule, subst))?;
if rule(root) {
subst(root)?;
}
Ok(())
}

/// Substitute [`ConstantPlaceholder`] nodes in `self` with [`Constant`]
/// Errors if a constant can not be found in `constants`
pub fn substitute_constants(&self, constants: &[Constant]) -> Result<Self, SigmaParsingError> {
self.try_rewrite_bu::<SigmaParsingError>(
|expr| matches!(expr, Expr::ConstPlaceholder(_)),
|expr| {
if let Expr::ConstPlaceholder(ConstantPlaceholder { id, tpe: _ }) = expr {
*expr = constants
.get(*id as usize)
.cloned()
.map(Expr::from)
.ok_or(SigmaParsingError::ConstantForPlaceholderNotFound(*id))?;
}
Ok(())
},
)
}

/// Prints the tree with newlines
pub fn debug_tree(&self) -> String {
let tree = format!("{:#?}", self);
Expand Down

0 comments on commit 002ec13

Please sign in to comment.