mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-01-08 21:58:00 -05:00
Based on commit 1dbe4db - Split into two crates, lib and cli - upgrade stwo, marked one stwo test `should_panic` @ShuangWu121 - various clippy and fmt fixes linked to the rust version update - bring all rust versions to 2025-05-14. CI still installs other versions for openvm which uses them internally. The stable rust version we test on is bumped to 1.85 - remove `examples` and related tests, which test the powdr crate on the previous version of powdr (since it uses another nightly). Happy to discuss this if it's important @leonardoalt
300 lines
10 KiB
Rust
300 lines
10 KiB
Rust
use std::{collections::BTreeMap, sync::Arc};
|
|
|
|
use itertools::Itertools;
|
|
use openvm_stark_backend::{
|
|
air_builders::symbolic::{
|
|
symbolic_expression::SymbolicExpression,
|
|
symbolic_variable::{Entry, SymbolicVariable},
|
|
SymbolicConstraints,
|
|
},
|
|
interaction::Interaction,
|
|
p3_field::PrimeField32,
|
|
};
|
|
use powdr::number::FieldElement;
|
|
use powdr::{
|
|
ast::analyzed::{
|
|
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicExpression, AlgebraicReference,
|
|
AlgebraicUnaryOperation, AlgebraicUnaryOperator, PolyID, PolynomialType,
|
|
},
|
|
number::BabyBearField,
|
|
};
|
|
|
|
pub fn algebraic_to_symbolic<T: PrimeField32>(
|
|
expr: &AlgebraicExpression<T>,
|
|
) -> SymbolicExpression<T> {
|
|
match expr {
|
|
AlgebraicExpression::Number(n) => SymbolicExpression::Constant(*n),
|
|
AlgebraicExpression::BinaryOperation(binary) => match binary.op {
|
|
AlgebraicBinaryOperator::Add => SymbolicExpression::Add {
|
|
x: Arc::new(algebraic_to_symbolic(&binary.left)),
|
|
y: Arc::new(algebraic_to_symbolic(&binary.right)),
|
|
degree_multiple: 0,
|
|
},
|
|
AlgebraicBinaryOperator::Sub => SymbolicExpression::Sub {
|
|
x: Arc::new(algebraic_to_symbolic(&binary.left)),
|
|
y: Arc::new(algebraic_to_symbolic(&binary.right)),
|
|
degree_multiple: 0,
|
|
},
|
|
AlgebraicBinaryOperator::Mul => SymbolicExpression::Mul {
|
|
x: Arc::new(algebraic_to_symbolic(&binary.left)),
|
|
y: Arc::new(algebraic_to_symbolic(&binary.right)),
|
|
degree_multiple: 0,
|
|
},
|
|
AlgebraicBinaryOperator::Pow => {
|
|
// Assuming the right operand is a constant number
|
|
let base = algebraic_to_symbolic(&binary.left);
|
|
let exp = match *binary.right {
|
|
AlgebraicExpression::Number(n) => n,
|
|
_ => unimplemented!(),
|
|
};
|
|
|
|
if exp == T::ZERO {
|
|
SymbolicExpression::Constant(T::ONE)
|
|
} else {
|
|
let mut result = base.clone();
|
|
let mut remaining = exp - T::ONE;
|
|
|
|
while remaining != T::ZERO {
|
|
result = SymbolicExpression::Mul {
|
|
x: Arc::new(result),
|
|
y: Arc::new(base.clone()),
|
|
degree_multiple: 0,
|
|
};
|
|
remaining -= T::ONE;
|
|
}
|
|
result
|
|
}
|
|
}
|
|
},
|
|
AlgebraicExpression::UnaryOperation(unary) => match unary.op {
|
|
AlgebraicUnaryOperator::Minus => SymbolicExpression::Neg {
|
|
x: Arc::new(algebraic_to_symbolic(&unary.expr)),
|
|
degree_multiple: 0,
|
|
},
|
|
},
|
|
AlgebraicExpression::Reference(algebraic_reference) => {
|
|
let poly_id = algebraic_reference.poly_id;
|
|
let next = algebraic_reference.next as usize;
|
|
match poly_id.ptype {
|
|
PolynomialType::Committed => SymbolicExpression::Variable(SymbolicVariable::new(
|
|
Entry::Main {
|
|
part_index: 0,
|
|
offset: next,
|
|
},
|
|
poly_id.id as usize,
|
|
)),
|
|
PolynomialType::Constant => SymbolicExpression::Variable(SymbolicVariable::new(
|
|
Entry::Preprocessed { offset: next },
|
|
poly_id.id as usize,
|
|
)),
|
|
PolynomialType::Intermediate => todo!(),
|
|
}
|
|
}
|
|
AlgebraicExpression::PublicReference(_) => {
|
|
unimplemented!()
|
|
}
|
|
AlgebraicExpression::Challenge(ch) => SymbolicExpression::Variable(SymbolicVariable::new(
|
|
Entry::Challenge,
|
|
ch.id.try_into().unwrap(),
|
|
)),
|
|
}
|
|
}
|
|
pub fn symbolic_to_algebraic<T: PrimeField32, P: FieldElement>(
|
|
expr: &SymbolicExpression<T>,
|
|
columns: &[String],
|
|
) -> AlgebraicExpression<P> {
|
|
match expr {
|
|
SymbolicExpression::Constant(c) => {
|
|
AlgebraicExpression::Number(P::from_bytes_le(&c.as_canonical_u32().to_le_bytes()))
|
|
}
|
|
SymbolicExpression::Add { x, y, .. } => {
|
|
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
|
left: Box::new(symbolic_to_algebraic(x, columns)),
|
|
right: Box::new(symbolic_to_algebraic(y, columns)),
|
|
op: AlgebraicBinaryOperator::Add,
|
|
})
|
|
}
|
|
SymbolicExpression::Sub { x, y, .. } => {
|
|
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
|
left: Box::new(symbolic_to_algebraic(x, columns)),
|
|
right: Box::new(symbolic_to_algebraic(y, columns)),
|
|
op: AlgebraicBinaryOperator::Sub,
|
|
})
|
|
}
|
|
SymbolicExpression::Mul { x, y, .. } => {
|
|
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
|
left: Box::new(symbolic_to_algebraic(x, columns)),
|
|
right: Box::new(symbolic_to_algebraic(y, columns)),
|
|
op: AlgebraicBinaryOperator::Mul,
|
|
})
|
|
}
|
|
SymbolicExpression::Neg { x, .. } => {
|
|
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation {
|
|
expr: Box::new(symbolic_to_algebraic(x, columns)),
|
|
op: AlgebraicUnaryOperator::Minus,
|
|
})
|
|
}
|
|
SymbolicExpression::Variable(SymbolicVariable { entry, index, .. }) => match entry {
|
|
Entry::Main { offset, part_index } => {
|
|
assert_eq!(*part_index, 0);
|
|
let next = match offset {
|
|
0 => false,
|
|
1 => true,
|
|
_ => unimplemented!(),
|
|
};
|
|
let name = columns.get(*index).unwrap_or_else(|| {
|
|
panic!("Column index out of bounds: {index}\nColumns: {columns:?}");
|
|
});
|
|
AlgebraicExpression::Reference(AlgebraicReference {
|
|
name: name.clone(),
|
|
poly_id: PolyID {
|
|
id: *index as u64,
|
|
ptype: PolynomialType::Committed,
|
|
},
|
|
next,
|
|
})
|
|
}
|
|
_ => unimplemented!(),
|
|
},
|
|
SymbolicExpression::IsFirstRow => AlgebraicExpression::Reference(AlgebraicReference {
|
|
name: "is_first_row".to_string(),
|
|
poly_id: PolyID {
|
|
id: 0,
|
|
ptype: PolynomialType::Constant,
|
|
},
|
|
next: false,
|
|
}),
|
|
SymbolicExpression::IsLastRow => AlgebraicExpression::Reference(AlgebraicReference {
|
|
name: "is_last_row".to_string(),
|
|
poly_id: PolyID {
|
|
id: 1,
|
|
ptype: PolynomialType::Constant,
|
|
},
|
|
next: false,
|
|
}),
|
|
SymbolicExpression::IsTransition => AlgebraicExpression::Reference(AlgebraicReference {
|
|
name: "is_transition".to_string(),
|
|
poly_id: PolyID {
|
|
id: 2,
|
|
ptype: PolynomialType::Constant,
|
|
},
|
|
next: false,
|
|
}),
|
|
}
|
|
}
|
|
|
|
pub fn get_pil<F: PrimeField32>(
|
|
name: &str,
|
|
constraints: &SymbolicConstraints<F>,
|
|
columns: &Vec<String>,
|
|
public_values: Vec<String>,
|
|
) -> String {
|
|
let mut pil = format!(
|
|
"
|
|
namespace {name};
|
|
// Preamble
|
|
col fixed is_first_row = [1] + [0]*;
|
|
col fixed is_last_row = [0] + [1]*;
|
|
col fixed is_transition = [0] + [1]* + [0];
|
|
|
|
"
|
|
);
|
|
|
|
let bus_id_to_name = [
|
|
(0, "EXECUTION_BRIDGE"),
|
|
(1, "MEMORY"),
|
|
(2, "PC_LOOKUP"),
|
|
(3, "VARIABLE_RANGE_CHECKER"),
|
|
(6, "BITWISE_LOOKUP"),
|
|
(7, "TUPLE_RANGE_CHECKER"),
|
|
]
|
|
.into_iter()
|
|
.collect::<BTreeMap<_, _>>();
|
|
|
|
pil.push_str(
|
|
&bus_id_to_name
|
|
.iter()
|
|
.map(|(id, name)| format!(" let {name} = {id};"))
|
|
.join("\n"),
|
|
);
|
|
|
|
pil.push_str(
|
|
"
|
|
|
|
// Witness columns
|
|
",
|
|
);
|
|
|
|
// Declare witness columns
|
|
for column in columns {
|
|
pil.push_str(&format!(" col witness {column};\n"));
|
|
}
|
|
|
|
let bus_interactions_by_bus = constraints
|
|
.interactions
|
|
.iter()
|
|
.map(|interaction| (interaction.bus_index, interaction))
|
|
.into_group_map()
|
|
.into_iter()
|
|
// Use BTreeMap to sort by bus_index
|
|
.collect::<BTreeMap<_, _>>();
|
|
|
|
pil.push_str(
|
|
"
|
|
// Bus interactions (bus_index, fields, count)\n",
|
|
);
|
|
|
|
for (bus_index, interactions) in bus_interactions_by_bus {
|
|
let bus_name = bus_id_to_name
|
|
.get(&bus_index)
|
|
.unwrap_or_else(|| panic!("Bus index {bus_index} not found in bus_id_to_name"));
|
|
|
|
for interaction in interactions {
|
|
format_bus_interaction(&mut pil, interaction, columns, &public_values, bus_name);
|
|
}
|
|
pil.push('\n');
|
|
}
|
|
|
|
pil.push_str(" // Constraints\n");
|
|
|
|
for constraint in &constraints.constraints {
|
|
pil.push_str(&format!(
|
|
" {} = 0;\n",
|
|
format_expr(constraint, columns, &public_values)
|
|
));
|
|
}
|
|
pil
|
|
}
|
|
|
|
fn format_bus_interaction<F: PrimeField32>(
|
|
pil: &mut String,
|
|
interaction: &Interaction<SymbolicExpression<F>>,
|
|
columns: &[String],
|
|
public_values: &[String],
|
|
bus_name: &str,
|
|
) {
|
|
let Interaction { message, count, .. } = interaction;
|
|
// We do not know what is a send or a receive
|
|
let function_name = "bus_interaction";
|
|
|
|
pil.push_str(&format!(
|
|
" std::protocols::bus::{}({bus_name}, [{}], {});\n",
|
|
function_name,
|
|
message
|
|
.iter()
|
|
.map(|value| format_expr(value, columns, public_values))
|
|
.collect::<Vec<String>>()
|
|
.join(", "),
|
|
format_expr(count, columns, public_values)
|
|
));
|
|
}
|
|
|
|
fn format_expr<F: PrimeField32>(
|
|
expr: &SymbolicExpression<F>,
|
|
columns: &[String],
|
|
// TODO: Implement public references
|
|
_public_values: &[String],
|
|
) -> String {
|
|
symbolic_to_algebraic::<_, BabyBearField>(expr, columns).to_string()
|
|
}
|