use std::collections::{HashMap, HashSet};
use crate::ir::dag::{ConstraintDag, DagNode, NodeId, TraceSource};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ColumnLeaf {
pub source: TraceSource,
pub col: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConstraintShape {
LinearWeightedSum,
General,
}
#[derive(Debug, Clone)]
pub struct ConstraintInfo {
pub constraint_idx: usize,
pub root: NodeId,
pub alpha_index: u32,
pub total_nodes: u32,
pub work: u32,
pub depth: u32,
pub column_leaves: HashSet<ColumnLeaf>,
pub shape: ConstraintShape,
}
impl ConstraintInfo {
pub fn leafset_size(&self) -> u32 {
self.column_leaves.len() as u32
}
}
pub fn analyze_constraints(dag: &ConstraintDag) -> Vec<ConstraintInfo> {
dag.constraints
.iter()
.enumerate()
.map(|(i, c)| analyze_one(dag, i, c.root, c.alpha_index))
.collect()
}
fn analyze_one(
dag: &ConstraintDag,
constraint_idx: usize,
root: NodeId,
alpha_index: u32,
) -> ConstraintInfo {
let mut depth_of: HashMap<NodeId, u32> = HashMap::new();
let mut column_leaves: HashSet<ColumnLeaf> = HashSet::new();
let mut work: u32 = 0;
let mut total: u32 = 0;
let depth = walk(dag, root, &mut depth_of, &mut column_leaves, &mut work, &mut total);
let shape = detect_shape(dag, root, &column_leaves);
ConstraintInfo {
constraint_idx,
root,
alpha_index,
total_nodes: total,
work,
depth,
column_leaves,
shape,
}
}
fn walk(
dag: &ConstraintDag,
reg: NodeId,
depth_of: &mut HashMap<NodeId, u32>,
column_leaves: &mut HashSet<ColumnLeaf>,
work: &mut u32,
total: &mut u32,
) -> u32 {
if let Some(&d) = depth_of.get(®) {
return d;
}
let node = &dag.nodes[reg as usize];
*total += 1;
let d = match *node {
DagNode::InputLeaf { source, col } => {
column_leaves.insert(ColumnLeaf { source, col });
0
}
DagNode::ConstF { .. }
| DagNode::ConstEF { .. }
| DagNode::PublicValue { .. }
| DagNode::GlobalCumulativeSum { .. }
| DagNode::IsFirstRow
| DagNode::IsLastRow
| DagNode::IsTransition => 0,
DagNode::AddF { a, b }
| DagNode::SubF { a, b }
| DagNode::MulF { a, b }
| DagNode::AddEF { a, b }
| DagNode::SubEF { a, b }
| DagNode::MulEF { a, b }
| DagNode::EFAddF { a, b }
| DagNode::EFSubF { a, b }
| DagNode::EFMulF { a, b } => {
*work += 1;
let da = walk(dag, a, depth_of, column_leaves, work, total);
let db = walk(dag, b, depth_of, column_leaves, work, total);
1 + da.max(db)
}
DagNode::NegF { a } | DagNode::NegEF { a } | DagNode::EFFromF { a } => {
*work += 1;
let da = walk(dag, a, depth_of, column_leaves, work, total);
1 + da
}
};
depth_of.insert(reg, d);
d
}
fn detect_shape(
dag: &ConstraintDag,
root: NodeId,
column_leaves: &HashSet<ColumnLeaf>,
) -> ConstraintShape {
if column_leaves.is_empty() {
return ConstraintShape::General;
}
let mut leaves_seen: HashSet<ColumnLeaf> = HashSet::new();
if !walk_linear_sum(dag, root, &mut leaves_seen) {
return ConstraintShape::General;
}
if leaves_seen == *column_leaves {
ConstraintShape::LinearWeightedSum
} else {
ConstraintShape::General
}
}
fn walk_linear_sum(
dag: &ConstraintDag,
node_id: NodeId,
leaves_seen: &mut HashSet<ColumnLeaf>,
) -> bool {
match dag.nodes[node_id as usize] {
DagNode::AddF { a, b } | DagNode::SubF { a, b } => {
walk_linear_sum(dag, a, leaves_seen) && walk_linear_sum(dag, b, leaves_seen)
}
DagNode::MulF { a, b } => match (coefficient(dag, a), coefficient(dag, b)) {
(Some(_), None) => {
extract_column_leaf(dag, b).map(|c| leaves_seen.insert(c)).unwrap_or(false)
}
(None, Some(_)) => {
extract_column_leaf(dag, a).map(|c| leaves_seen.insert(c)).unwrap_or(false)
}
_ => false,
},
DagNode::InputLeaf { source, col } => leaves_seen.insert(ColumnLeaf { source, col }),
_ => false,
}
}
fn coefficient(dag: &ConstraintDag, node_id: NodeId) -> Option<()> {
match dag.nodes[node_id as usize] {
DagNode::ConstF { .. }
| DagNode::ConstEF { .. }
| DagNode::PublicValue { .. }
| DagNode::GlobalCumulativeSum { .. }
| DagNode::IsFirstRow
| DagNode::IsLastRow
| DagNode::IsTransition => Some(()),
_ => None,
}
}
fn extract_column_leaf(dag: &ConstraintDag, node_id: NodeId) -> Option<ColumnLeaf> {
match dag.nodes[node_id as usize] {
DagNode::InputLeaf { source, col } => Some(ColumnLeaf { source, col }),
_ => None,
}
}