Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"can process fully" #2255

Merged
merged 8 commits into from
Dec 18, 2024
Merged
32 changes: 32 additions & 0 deletions executor/src/witgen/machines/double_sorted_witness_machine_32.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::collections::{BTreeMap, HashMap, HashSet};
use std::iter::once;

use bit_vec::BitVec;
use itertools::Itertools;

use super::{LookupCell, Machine, MachineParts};
use crate::witgen::data_structures::caller_data::CallerData;
use crate::witgen::data_structures::mutable_state::MutableState;
use crate::witgen::machines::compute_size_and_log;
use crate::witgen::processor::OuterQuery;
use crate::witgen::range_constraints::RangeConstraint;
use crate::witgen::rows::RowPair;
use crate::witgen::util::try_to_simple_poly;
use crate::witgen::{EvalError, EvalResult, EvalValue, FixedData, IncompleteCause, QueryCallback};
Expand Down Expand Up @@ -185,6 +187,36 @@ impl<'a, T: FieldElement> DoubleSortedWitnesses32<'a, T> {
}

impl<'a, T: FieldElement> Machine<'a, T> for DoubleSortedWitnesses32<'a, T> {
fn can_process_call_fully(
&mut self,
identity_id: u64,
known_arguments: &BitVec,
range_constraints: &[Option<RangeConstraint<T>>],
) -> bool {
assert!(self.parts.connections.contains_key(&identity_id));
assert_eq!(known_arguments.len(), 4);
assert_eq!(range_constraints.len(), 4);

// We blindly assume the parameters to be operation_id, addr, step, value

// We need to known operation_id, step and address for all calls.
if !known_arguments[0] || !known_arguments[1] || !known_arguments[2] {
return false;
}

// For the value, it depends: If we write, we need to know it, if we read we do not need to know it.
if known_arguments[3] {
// It is known, so we are good anyway.
true
} else {
// It is not known, so we can only process if we do not write.
range_constraints[0].as_ref().map_or(false, |rc| {
!rc.allows_value(T::from(OPERATION_ID_BOOTLOADER_WRITE))
&& !rc.allows_value(T::from(OPERATION_ID_WRITE))
})
}
}

fn process_lookup_direct<'b, 'c, Q: QueryCallback<T>>(
&mut self,
_mutable_state: &'b MutableState<'a, T, Q>,
Expand Down
23 changes: 23 additions & 0 deletions executor/src/witgen/machines/fixed_lookup_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ fn create_index<T: FieldElement>(
connections: &BTreeMap<u64, Connection<'_, T>>,
) -> HashMap<Vec<T>, IndexValue<T>> {
let right = connections[&application.identity_id].right;
assert!(right.selector.is_one());

let (input_fixed_columns, output_fixed_columns): (Vec<_>, Vec<_>) = right
.expressions
Expand Down Expand Up @@ -297,6 +298,28 @@ impl<'a, T: FieldElement> Machine<'a, T> for FixedLookup<'a, T> {
"FixedLookup"
}

fn can_process_call_fully(
&mut self,
identity_id: u64,
known_arguments: &BitVec,
_range_constraints: &[Option<RangeConstraint<T>>],
) -> bool {
if !Self::is_responsible(&self.connections[&identity_id]) {
return false;
}
let index = self
.indices
.entry(Application {
identity_id,
inputs: known_arguments.clone(),
})
.or_insert_with_key(|application| {
create_index(self.fixed_data, application, &self.connections)
});
// Check that if there is a match, it is unique.
index.values().all(|value| value.0.is_some())
}

fn process_plookup<Q: crate::witgen::QueryCallback<T>>(
&mut self,
mutable_state: &MutableState<'a, T, Q>,
Expand Down
53 changes: 53 additions & 0 deletions executor/src/witgen/machines/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt::Display;

use bit_vec::BitVec;
use dynamic_machine::DynamicMachine;
use powdr_ast::analyzed::{
self, AlgebraicExpression, DegreeRange, PermutationIdentity, PhantomPermutationIdentity, PolyID,
Expand All @@ -21,6 +22,7 @@ use self::second_stage_machine::SecondStageMachine;
use self::sorted_witness_machine::SortedWitnesses;
use self::write_once_memory::WriteOnceMemory;

use super::range_constraints::RangeConstraint;
use super::rows::RowPair;
use super::{EvalError, EvalResult, FixedData, QueryCallback};

Expand Down Expand Up @@ -53,6 +55,23 @@ pub trait Machine<'a, T: FieldElement>: Send + Sync {
);
}

/// Returns true if this machine can alway fully process a call via the given
/// identity, the set of known arguments and a list of range constraints
/// on the parameters. Note that the range constraints can be imposed both
/// on inputs and on outputs.
/// If this returns true, then corresponding calls to `process_lookup_direct`
/// are safe.
/// The function requires `&mut self` because it usually builds an index structure
/// or something similar.
fn can_process_call_fully(
&mut self,
_identity_id: u64,
_known_arguments: &BitVec,
_range_constraints: &[Option<RangeConstraint<T>>],
) -> bool {
false
}

/// Like `process_plookup`, but also records the time spent in this machine.
fn process_plookup_timed<'b, Q: QueryCallback<T>>(
&mut self,
Expand Down Expand Up @@ -155,6 +174,40 @@ impl<'a, T: FieldElement> Machine<'a, T> for KnownMachine<'a, T> {
}
}

fn can_process_call_fully(
&mut self,
identity_id: u64,
known_arguments: &BitVec,
range_constraints: &[Option<RangeConstraint<T>>],
) -> bool {
match self {
KnownMachine::SecondStageMachine(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
KnownMachine::SortedWitnesses(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
KnownMachine::DoubleSortedWitnesses16(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
KnownMachine::DoubleSortedWitnesses32(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
KnownMachine::WriteOnceMemory(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
KnownMachine::BlockMachine(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
KnownMachine::DynamicMachine(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
KnownMachine::FixedLookup(m) => {
m.can_process_call_fully(identity_id, known_arguments, range_constraints)
}
}
}

fn process_plookup<'b, Q: QueryCallback<T>>(
&mut self,
mutable_state: &'b MutableState<'a, T, Q>,
Expand Down
Loading