mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-04-20 03:03:25 -04:00
266 lines
8.1 KiB
Rust
266 lines
8.1 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use number::FieldElement;
|
|
|
|
use super::affine_expression::AffineExpression;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SequenceStep {
|
|
pub row_delta: i64,
|
|
pub identity: IdentityInSequence,
|
|
}
|
|
|
|
/// Goes through all rows of the block machine (plus the ones before and after)
|
|
/// forward, backward, and forward again.
|
|
/// In each row, iterates over all identities until no further progress is made.
|
|
pub struct DefaultSequenceIterator {
|
|
identities_count: usize,
|
|
row_deltas: Vec<i64>,
|
|
outer_query_row: Option<i64>,
|
|
|
|
/// Whether this is the first time the iterator is called.
|
|
is_first: bool,
|
|
/// Whether any progress was made in the current round.
|
|
progress_in_current_round: bool,
|
|
/// The current row delta index.
|
|
cur_row_delta_index: usize,
|
|
/// The current identity index.
|
|
cur_identity_index: usize,
|
|
/// The number of rounds for the current row delta.
|
|
/// If this number gets too large, we will assume that we're in an infinite loop and exit.
|
|
current_round_count: usize,
|
|
|
|
/// The steps on which we made progress.
|
|
progress_steps: Vec<SequenceStep>,
|
|
}
|
|
|
|
const MAX_ROUNDS_PER_ROW_DELTA: usize = 100;
|
|
|
|
impl DefaultSequenceIterator {
|
|
pub fn new(block_size: usize, identities_count: usize, outer_query_row: Option<i64>) -> Self {
|
|
let max_row = block_size as i64 - 1;
|
|
DefaultSequenceIterator {
|
|
identities_count,
|
|
row_deltas: (-1..=max_row)
|
|
.chain((-1..max_row).rev())
|
|
.chain(0..=max_row)
|
|
.collect(),
|
|
outer_query_row,
|
|
is_first: true,
|
|
progress_in_current_round: false,
|
|
cur_row_delta_index: 0,
|
|
cur_identity_index: 0,
|
|
current_round_count: 0,
|
|
progress_steps: vec![],
|
|
}
|
|
}
|
|
|
|
/// Update the state of the iterator.
|
|
/// If we're not at the last identity in the current row, just moves to the next.
|
|
/// Otherwise, starts with identity 0 and moves to the next row if no progress was made.
|
|
fn update_state(&mut self) {
|
|
if !self.is_first {
|
|
if self.is_last_identity() {
|
|
self.start_next_round();
|
|
} else {
|
|
// Stay at row delta, move to next identity.
|
|
self.cur_identity_index += 1;
|
|
}
|
|
}
|
|
self.is_first = false;
|
|
}
|
|
|
|
fn is_last_identity(&self) -> bool {
|
|
let row_delta = self.row_deltas[self.cur_row_delta_index];
|
|
let is_on_row_with_outer_query = self.outer_query_row == Some(row_delta);
|
|
|
|
if is_on_row_with_outer_query {
|
|
// In the last row, we want to process one more identity, the outer query.
|
|
self.cur_identity_index == self.identities_count
|
|
} else {
|
|
self.cur_identity_index == self.identities_count - 1
|
|
}
|
|
}
|
|
|
|
fn start_next_round(&mut self) {
|
|
if !self.progress_in_current_round || self.current_round_count > MAX_ROUNDS_PER_ROW_DELTA {
|
|
// Move to next row delta, starting with identity 0.
|
|
if self.current_round_count > MAX_ROUNDS_PER_ROW_DELTA {
|
|
panic!("In witness generation for block machine, we have been stuck in the same row for {MAX_ROUNDS_PER_ROW_DELTA} rounds. \
|
|
This is a bug in the witness generation algorithm.");
|
|
}
|
|
|
|
self.cur_row_delta_index += 1;
|
|
self.current_round_count = 0;
|
|
} else {
|
|
// Stay and current row delta, starting with identity 0.
|
|
self.current_round_count += 1;
|
|
}
|
|
self.cur_identity_index = 0;
|
|
self.progress_in_current_round = false;
|
|
}
|
|
|
|
pub fn report_progress(&mut self, progress_in_last_step: bool) {
|
|
assert!(!self.is_first, "Called report_progress() before next()");
|
|
|
|
if progress_in_last_step {
|
|
self.progress_steps.push(self.current_step());
|
|
}
|
|
self.progress_in_current_round |= progress_in_last_step;
|
|
}
|
|
|
|
pub fn next(&mut self) -> Option<SequenceStep> {
|
|
self.update_state();
|
|
|
|
if self.cur_row_delta_index == self.row_deltas.len() || self.identities_count == 0 {
|
|
// Done!
|
|
return None;
|
|
}
|
|
|
|
Some(self.current_step())
|
|
}
|
|
|
|
fn current_step(&self) -> SequenceStep {
|
|
SequenceStep {
|
|
row_delta: self.row_deltas[self.cur_row_delta_index],
|
|
identity: if self.cur_identity_index < self.identities_count {
|
|
IdentityInSequence::Internal(self.cur_identity_index)
|
|
} else {
|
|
IdentityInSequence::OuterQuery
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum IdentityInSequence {
|
|
Internal(usize),
|
|
OuterQuery,
|
|
}
|
|
|
|
#[derive(PartialOrd, Ord, PartialEq, Eq, Debug)]
|
|
pub struct SequenceCacheKey {
|
|
/// For each expression on the left-hand side of the lookup, whether it is a constant.
|
|
known_columns: Vec<bool>,
|
|
}
|
|
|
|
impl<K, T> From<&[AffineExpression<K, T>]> for SequenceCacheKey
|
|
where
|
|
K: Copy + Ord,
|
|
T: FieldElement,
|
|
{
|
|
fn from(value: &[AffineExpression<K, T>]) -> Self {
|
|
SequenceCacheKey {
|
|
known_columns: value.iter().map(|v| v.is_constant()).collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum ProcessingSequenceIterator {
|
|
Default(DefaultSequenceIterator),
|
|
Cached(<Vec<SequenceStep> as IntoIterator>::IntoIter),
|
|
}
|
|
|
|
impl ProcessingSequenceIterator {
|
|
pub fn report_progress(&mut self, progress_in_last_step: bool) {
|
|
match self {
|
|
Self::Default(it) => it.report_progress(progress_in_last_step),
|
|
Self::Cached(_) => {} // Progress is ignored
|
|
}
|
|
}
|
|
|
|
pub fn has_steps(&self) -> bool {
|
|
match self {
|
|
Self::Default(_) => true,
|
|
Self::Cached(it) => it.len() > 0,
|
|
}
|
|
}
|
|
|
|
pub fn is_cached(&self) -> bool {
|
|
match self {
|
|
Self::Default(_) => false,
|
|
Self::Cached(_) => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Iterator for ProcessingSequenceIterator {
|
|
type Item = SequenceStep;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
match self {
|
|
Self::Default(it) => it.next(),
|
|
Self::Cached(it) => it.next(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ProcessingSequenceCache {
|
|
block_size: usize,
|
|
identities_count: usize,
|
|
cache: BTreeMap<SequenceCacheKey, Vec<SequenceStep>>,
|
|
}
|
|
|
|
impl ProcessingSequenceCache {
|
|
pub fn new(block_size: usize, identities_count: usize) -> Self {
|
|
ProcessingSequenceCache {
|
|
block_size,
|
|
identities_count,
|
|
cache: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn get_processing_sequence<K, T>(
|
|
&self,
|
|
left: &[AffineExpression<K, T>],
|
|
) -> ProcessingSequenceIterator
|
|
where
|
|
K: Copy + Ord,
|
|
T: FieldElement,
|
|
{
|
|
match self.cache.get(&left.into()) {
|
|
Some(cached_sequence) => {
|
|
log::trace!("Using cached sequence");
|
|
ProcessingSequenceIterator::Cached(cached_sequence.clone().into_iter())
|
|
}
|
|
None => {
|
|
log::trace!("Using default sequence");
|
|
self.get_default_sequence_iterator()
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_default_sequence_iterator(&self) -> ProcessingSequenceIterator {
|
|
ProcessingSequenceIterator::Default(DefaultSequenceIterator::new(
|
|
self.block_size,
|
|
self.identities_count,
|
|
// Run the outer query on the last row of the block.
|
|
Some(self.block_size as i64 - 1),
|
|
))
|
|
}
|
|
|
|
pub fn report_incomplete<K, T>(&mut self, left: &[AffineExpression<K, T>])
|
|
where
|
|
K: Copy + Ord,
|
|
T: FieldElement,
|
|
{
|
|
self.cache.entry(left.into()).or_insert(vec![]);
|
|
}
|
|
|
|
pub fn report_processing_sequence<K, T>(
|
|
&mut self,
|
|
left: &[AffineExpression<K, T>],
|
|
sequence_iterator: ProcessingSequenceIterator,
|
|
) where
|
|
K: Copy + Ord,
|
|
T: FieldElement,
|
|
{
|
|
match sequence_iterator {
|
|
ProcessingSequenceIterator::Default(it) => {
|
|
self.cache.entry(left.into()).or_insert(it.progress_steps);
|
|
}
|
|
ProcessingSequenceIterator::Cached(_) => {} // Already cached, do nothing
|
|
}
|
|
}
|
|
}
|