use {
crate::witness::{
scheduling::DependencyInfo, Layer, LayerType, LayeredWitnessBuilders, WitnessBuilder,
},
std::{
collections::{HashSet, VecDeque},
mem::take,
},
};
pub struct LayerScheduler<'a> {
witness_builders: &'a [WitnessBuilder],
deps: DependencyInfo,
frontier: VecDeque<usize>,
processed: Vec<bool>,
pre_layers: Vec<Vec<usize>>,
inverse_batches: Vec<Vec<usize>>,
current_pre_segment: Vec<usize>,
pending_inverse_outputs: HashSet<usize>,
pending_inverses: Vec<usize>,
}
impl<'a> LayerScheduler<'a> {
pub fn new(witness_builders: &'a [WitnessBuilder]) -> Self {
let deps = DependencyInfo::new(witness_builders);
let frontier = (0..witness_builders.len())
.filter(|&i| deps.in_degrees[i] == 0)
.collect();
Self {
witness_builders,
deps,
frontier,
processed: vec![false; witness_builders.len()],
pre_layers: Vec::new(),
inverse_batches: Vec::new(),
current_pre_segment: Vec::new(),
pending_inverse_outputs: HashSet::new(),
pending_inverses: Vec::new(),
}
}
pub fn build_layers(mut self) -> LayeredWitnessBuilders {
while !self.frontier.is_empty() || !self.pending_inverses.is_empty() {
if !self.process_current_frontier() {
if !self.pending_inverses.is_empty() {
self.flush_layer();
} else {
break;
}
}
}
if !self.current_pre_segment.is_empty() || !self.pending_inverses.is_empty() {
self.flush_layer();
}
self.build_result()
}
fn process_current_frontier(&mut self) -> bool {
let initial_frontier_size = self.frontier.len();
let mut deferred = VecDeque::new();
while let Some(node_idx) = self.frontier.pop_front() {
if self.processed[node_idx] {
continue;
}
if self.can_process_now(node_idx) {
self.process_node(node_idx);
} else {
deferred.push_back(node_idx);
}
}
self.frontier = deferred;
self.frontier.len() < initial_frontier_size
}
#[inline]
fn can_process_now(&self, node_idx: usize) -> bool {
!self.deps.reads[node_idx]
.iter()
.any(|&witness| self.pending_inverse_outputs.contains(&witness))
}
fn process_node(&mut self, node_idx: usize) {
match &self.witness_builders[node_idx] {
WitnessBuilder::Inverse(out_witness, _)
| WitnessBuilder::LogUpInverse(out_witness, ..)
| WitnessBuilder::CombinedTableEntryInverse(
crate::witness::CombinedTableEntryInverseData {
idx: out_witness, ..
},
)
| WitnessBuilder::SpreadTableQuotient {
idx: out_witness, ..
} => {
self.pending_inverses.push(node_idx);
self.pending_inverse_outputs.insert(*out_witness);
}
_ => {
self.current_pre_segment.push(node_idx);
self.mark_processed(node_idx);
}
}
}
fn mark_processed(&mut self, node_idx: usize) {
self.processed[node_idx] = true;
let dependents = self.deps.adjacency_list[node_idx].clone();
for dependent in dependents {
self.deps.in_degrees[dependent] -= 1;
if self.deps.in_degrees[dependent] == 0 {
self.frontier.push_back(dependent);
}
}
}
fn flush_layer(&mut self) {
self.pre_layers.push(take(&mut self.current_pre_segment));
let inverse_batch = take(&mut self.pending_inverses);
for &inverse_idx in &inverse_batch {
if !self.processed[inverse_idx] {
self.mark_processed(inverse_idx);
}
}
self.inverse_batches.push(inverse_batch);
self.pending_inverse_outputs.clear();
}
fn build_result(self) -> LayeredWitnessBuilders {
let mut layers = Vec::new();
for (pre_layer, inverse_batch) in self.pre_layers.iter().zip(&self.inverse_batches) {
if !pre_layer.is_empty() {
let witness_builders = pre_layer
.iter()
.map(|&builder_idx| self.witness_builders[builder_idx].clone())
.collect();
layers.push(Layer {
witness_builders,
typ: LayerType::Other,
});
}
if !inverse_batch.is_empty() {
let witness_builders = inverse_batch
.iter()
.map(|&inverse_idx| self.witness_builders[inverse_idx].clone())
.collect();
layers.push(Layer {
witness_builders,
typ: LayerType::Inverse,
});
}
}
LayeredWitnessBuilders { layers }
}
}