Skip to content
Open
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ hashbrown = { version = "0.14.3", default-features = false, features = [
pest = "2.8.0"
pest_derive = "2.8.0"
petgraph = "0.6"
rayon = "1.10"
directories = { version = "6.0.0", optional = true }
minicbor-serde = { version = "0.5.0", features = ["std"], optional = true }
serde_bytes = "0.11"
Expand Down
2 changes: 1 addition & 1 deletion src/backends/plonky2/circuits/mainpod/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1574,7 +1574,7 @@ fn test_normalize_st_tmpl_self_predicate_hash() -> Result<()> {
vec!["x".to_string()],
)
.unwrap();
cpb.predicates.push(pred_b);
cpb.push_predicate(pred_b);
let batch = cpb.finish().unwrap();

// Compute the expected resolved hash of pred_A
Expand Down
46 changes: 31 additions & 15 deletions src/frontend/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ impl StatementTmplBuilder {
pub struct CustomPredicateBatchBuilder {
params: Params,
pub name: String,
pub predicates: Vec<CustomPredicate>,
/// Private so every insertion goes through `predicate()` or
/// `push_predicate()`, which keep the name index in step.
predicates: Vec<CustomPredicate>,
/// Name -> index over `predicates`, so name lookups don't rescan the
/// whole batch. On a duplicate name the first index wins, matching a
/// front-to-back scan.
predicate_index_by_name: HashMap<String, usize>,
/// Forward references to resolve in finish(): (predicate_idx, statement_idx, arg_idx, name)
pending_self_pred_hashes: Vec<(usize, usize, usize, String)>,
}
Expand All @@ -142,10 +148,24 @@ impl CustomPredicateBatchBuilder {
params,
name,
predicates: Vec::new(),
predicate_index_by_name: HashMap::new(),
pending_self_pred_hashes: Vec::new(),
}
}

pub fn predicates(&self) -> &[CustomPredicate] {
&self.predicates
}

/// Append a prebuilt predicate, skipping the checks `predicate()`
/// performs.
pub fn push_predicate(&mut self, predicate: CustomPredicate) {
self.predicate_index_by_name
.entry(predicate.name.clone())
.or_insert(self.predicates.len());
self.predicates.push(predicate);
}

pub fn predicate_and(
&mut self,
name: &str,
Expand Down Expand Up @@ -176,7 +196,7 @@ impl CustomPredicateBatchBuilder {
priv_args: &[&str],
sts: &[StatementTmplBuilder],
) -> Result<Predicate> {
if self.predicates.iter().any(|p| p.name == name) {
if self.predicate_index_by_name.contains_key(name) {
return Err(Error::custom(format!(
"Duplicate predicate name '{}' in batch",
name
Expand Down Expand Up @@ -228,8 +248,8 @@ impl CustomPredicateBatchBuilder {
}
BuilderArg::SelfPredicateHash(pred_name) => {
// Try backward reference first
match self.predicates.iter().position(|p| p.name == *pred_name) {
Some(index) => StatementTmplArg::SelfPredicateHash(index),
match self.predicate_index_by_name.get(pred_name) {
Some(&index) => StatementTmplArg::SelfPredicateHash(index),
None => {
// Forward reference - placeholder, resolved in finish()
pending.push((
Expand Down Expand Up @@ -270,24 +290,20 @@ impl CustomPredicateBatchBuilder {
.map(|s| s.to_string())
.collect(),
)?;
self.predicates.push(custom_predicate);
self.push_predicate(custom_predicate);
self.pending_self_pred_hashes.extend(pending);
Ok(Predicate::BatchSelf(self.predicates.len() - 1))
}

pub fn finish(mut self) -> Result<Arc<CustomPredicateBatch>> {
// Resolve forward references for SelfPredicateHash
for (pred_idx, stmt_idx, arg_idx, ref name) in &self.pending_self_pred_hashes {
let target_idx = self
.predicates
.iter()
.position(|p| p.name == *name)
.ok_or_else(|| {
Error::custom(format!(
"SelfPredicateHash references unknown predicate '{}'",
name
))
})?;
let target_idx = *self.predicate_index_by_name.get(name).ok_or_else(|| {
Error::custom(format!(
"SelfPredicateHash references unknown predicate '{}'",
name
))
})?;
self.predicates[*pred_idx].statements[*stmt_idx].args[*arg_idx] =
StatementTmplArg::SelfPredicateHash(target_idx);
}
Expand Down
26 changes: 9 additions & 17 deletions src/lang/frontend_ast_lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ impl<'a> Lowerer<'a> {
&self,
) -> Result<Vec<frontend_ast_split::SplitResult>, LoweringError> {
let doc = self.validated.document();
let predicates: Vec<CustomPredicateDef> = doc
let mut predicates: Vec<CustomPredicateDef> = doc
.items
.iter()
.filter_map(|item| match item {
Expand All @@ -548,23 +548,15 @@ impl<'a> Lowerer<'a> {
})
.collect();

// Apply splitting to each predicate as needed. The typed-key rewrite
// happens before splitting so split chain pieces inherit `Index` keys
// unchanged. The search cache is shared across the module: modules
// routinely contain families of same-shape predicates, which then
// pay for the ordering search once.
let split_started = std::time::Instant::now();
let mut search_cache = frontend_ast_split::SplitSearchCache::default();
let mut split_results = Vec::new();
for mut pred in predicates {
self.rewrite_typed_dot_access(&mut pred);
let result = frontend_ast_split::split_predicate_if_needed(
pred,
self.params,
&mut search_cache,
)?;
split_results.push(result);
// The typed-key rewrite happens before splitting so split chain
// pieces inherit `Index` keys unchanged.
for pred in &mut predicates {
self.rewrite_typed_dot_access(pred);
}

let split_started = std::time::Instant::now();
let split_results =
frontend_ast_split::split_predicates_if_needed(predicates, self.params)?;
log::debug!(
"predicate splitting: {:?} ({} predicates)",
split_started.elapsed(),
Expand Down
Loading
Loading