mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-01-09 17:48:21 -05:00
New expression type (#2831)
This PR adds a new `powdr_expression` crate, containing a simplified version of `powdr_ast::analyzed::AlgebraicExpression`. The idea here is that this decouples our Autoprecompiles code from the PowdrVM & PIL codebase. This will allow us to gradually change the expression type according to our needs. See #2832 and #2833 as work-in-progress examples. Differences to previous `AlgebraicExpression`: - Generic over the reference type (but `powdr_autoprecompiles::legacy_expression` adds `AlgebraicReference` and defines `type AlgebraicExpression<T> = ActualAlgebraicExpression<T, AlgebraicReference>`, for now) - Removed `AlgebraicExpression::{PublicReference,Challenge}` variants - Removed `AlgebraicBinaryOperator::Pow` variant - `degree_with_cache()` -> `degree()`, as we don't have intermediate polynomials anymore I included a `powdr_ast::analyzed::AlgebraicExpression <--> powdr_autoprecompiles::legacy_expression::AlgebraicExpression` conversion, which is only used to interact with the `powdr_pilopt` crate, which still is used by the APC codebase and the PowdrVM / PIL codebase.
This commit is contained in:
@@ -11,6 +11,7 @@ members = [
|
||||
"cli-rs",
|
||||
"constraint-solver",
|
||||
"executor",
|
||||
"expression",
|
||||
"jit-compiler",
|
||||
"riscv",
|
||||
"riscv-elf",
|
||||
@@ -61,6 +62,7 @@ powdr-backend = { path = "./backend", version = "0.1.4" }
|
||||
powdr-backend-utils = { path = "./backend-utils", version = "0.1.4" }
|
||||
powdr-executor = { path = "./executor", version = "0.1.4" }
|
||||
powdr-executor-utils = { path = "./executor-utils", version = "0.1.4" }
|
||||
powdr-expression = { path = "./expression", version = "0.1.4" }
|
||||
powdr-importer = { path = "./importer", version = "0.1.4" }
|
||||
powdr-jit-compiler = { path = "./jit-compiler", version = "0.1.4" }
|
||||
powdr-linker = { path = "./linker", version = "0.1.4" }
|
||||
|
||||
@@ -8,6 +8,7 @@ repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
powdr-ast.workspace = true
|
||||
powdr-expression.workspace = true
|
||||
powdr-number.workspace = true
|
||||
powdr-pil-analyzer.workspace = true
|
||||
powdr-pilopt.workspace = true
|
||||
|
||||
228
autoprecompiles/src/legacy_expression.rs
Normal file
228
autoprecompiles/src/legacy_expression.rs
Normal file
@@ -0,0 +1,228 @@
|
||||
//! In this module, we instantiate `powdr_expression::AlgebraicExpression` using a
|
||||
//! custom `AlgebraicReference` type. This makes the type very similar to the
|
||||
//! `powdr_ast::analyzed::AlgebraicExpression`, which we've used historically.
|
||||
//! Going forward, we will simplify the code and remove this module eventually.
|
||||
use powdr_expression::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicUnaryOperation,
|
||||
AlgebraicUnaryOperator,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
pub type AlgebraicExpression<T> = powdr_expression::AlgebraicExpression<T, AlgebraicReference>;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub enum PolynomialType {
|
||||
Committed,
|
||||
Constant,
|
||||
Intermediate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PolyID {
|
||||
pub id: u64,
|
||||
pub ptype: PolynomialType,
|
||||
}
|
||||
|
||||
impl Hash for PolyID {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
// single call to hash is faster
|
||||
((self.id << 2) + self.ptype as u64).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
|
||||
pub struct AlgebraicReference {
|
||||
/// Name of the polynomial - just for informational purposes.
|
||||
/// Comparisons are based on polynomial ID.
|
||||
/// In case of an array element, this ends in `[i]`.
|
||||
pub name: String,
|
||||
/// Identifier for a polynomial reference, already contains
|
||||
/// the element offset in case of an array element.
|
||||
pub poly_id: PolyID,
|
||||
pub next: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AlgebraicReference {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}{}", self.name, if self.next { "'" } else { "" },)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for AlgebraicReference {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for AlgebraicReference {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
(&self.poly_id, &self.next).cmp(&(&other.poly_id, &other.next))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for AlgebraicReference {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.poly_id == other.poly_id && self.next == other.next
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for AlgebraicReference {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.poly_id.hash(state);
|
||||
self.next.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion to / from powdr_ast::analyzed::AlgebraicExpression.
|
||||
pub mod ast_compatibility {
|
||||
|
||||
use super::*;
|
||||
|
||||
impl From<AlgebraicReference> for powdr_ast::analyzed::AlgebraicReference {
|
||||
fn from(reference: AlgebraicReference) -> Self {
|
||||
powdr_ast::analyzed::AlgebraicReference {
|
||||
name: reference.name,
|
||||
poly_id: powdr_ast::analyzed::PolyID {
|
||||
id: reference.poly_id.id,
|
||||
ptype: match reference.poly_id.ptype {
|
||||
PolynomialType::Committed => powdr_ast::analyzed::PolynomialType::Committed,
|
||||
PolynomialType::Constant => powdr_ast::analyzed::PolynomialType::Constant,
|
||||
PolynomialType::Intermediate => {
|
||||
powdr_ast::analyzed::PolynomialType::Intermediate
|
||||
}
|
||||
},
|
||||
},
|
||||
next: reference.next,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<powdr_ast::analyzed::AlgebraicReference> for AlgebraicReference {
|
||||
fn from(reference: powdr_ast::analyzed::AlgebraicReference) -> Self {
|
||||
AlgebraicReference {
|
||||
name: reference.name,
|
||||
poly_id: PolyID {
|
||||
id: reference.poly_id.id,
|
||||
ptype: match reference.poly_id.ptype {
|
||||
powdr_ast::analyzed::PolynomialType::Committed => PolynomialType::Committed,
|
||||
powdr_ast::analyzed::PolynomialType::Constant => PolynomialType::Constant,
|
||||
powdr_ast::analyzed::PolynomialType::Intermediate => {
|
||||
PolynomialType::Intermediate
|
||||
}
|
||||
},
|
||||
},
|
||||
next: reference.next,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CompatibleWithAstExpression<T>: Sized {
|
||||
/// Converts the expression into an AST expression.
|
||||
fn into_ast_expression(self) -> powdr_ast::analyzed::AlgebraicExpression<T>;
|
||||
|
||||
/// Attempts to convert an AST expression into this expression type.
|
||||
/// Returns None if there is a public reference, a reference to a challenge, or an exponentiation.
|
||||
fn try_from_ast_expression(
|
||||
expr: powdr_ast::analyzed::AlgebraicExpression<T>,
|
||||
) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl<T> CompatibleWithAstExpression<T> for AlgebraicExpression<T> {
|
||||
fn into_ast_expression(self) -> powdr_ast::analyzed::AlgebraicExpression<T> {
|
||||
match self {
|
||||
AlgebraicExpression::Number(n) => {
|
||||
powdr_ast::analyzed::AlgebraicExpression::Number(n)
|
||||
}
|
||||
AlgebraicExpression::Reference(reference) => {
|
||||
powdr_ast::analyzed::AlgebraicExpression::Reference(reference.into())
|
||||
}
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
||||
left,
|
||||
op,
|
||||
right,
|
||||
}) => {
|
||||
let left = left.into_ast_expression();
|
||||
let right = right.into_ast_expression();
|
||||
let op = match op {
|
||||
AlgebraicBinaryOperator::Add => {
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperator::Add
|
||||
}
|
||||
AlgebraicBinaryOperator::Sub => {
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperator::Sub
|
||||
}
|
||||
AlgebraicBinaryOperator::Mul => {
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperator::Mul
|
||||
}
|
||||
};
|
||||
powdr_ast::analyzed::AlgebraicExpression::BinaryOperation(
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperation {
|
||||
left: Box::new(left),
|
||||
op,
|
||||
right: Box::new(right),
|
||||
},
|
||||
)
|
||||
}
|
||||
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { op, expr }) => {
|
||||
powdr_ast::analyzed::AlgebraicExpression::UnaryOperation(
|
||||
powdr_ast::analyzed::AlgebraicUnaryOperation {
|
||||
op: match op {
|
||||
AlgebraicUnaryOperator::Minus => {
|
||||
powdr_ast::analyzed::AlgebraicUnaryOperator::Minus
|
||||
}
|
||||
},
|
||||
expr: Box::new(expr.into_ast_expression()),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_from_ast_expression(
|
||||
expr: powdr_ast::analyzed::AlgebraicExpression<T>,
|
||||
) -> Option<Self> {
|
||||
match expr {
|
||||
powdr_ast::analyzed::AlgebraicExpression::Number(n) => {
|
||||
Some(AlgebraicExpression::Number(n))
|
||||
}
|
||||
powdr_ast::analyzed::AlgebraicExpression::Reference(reference) => {
|
||||
Some(AlgebraicExpression::Reference(reference.into()))
|
||||
}
|
||||
powdr_ast::analyzed::AlgebraicExpression::BinaryOperation(
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperation { left, op, right },
|
||||
) => {
|
||||
let left = AlgebraicExpression::try_from_ast_expression(*left)?;
|
||||
let right = AlgebraicExpression::try_from_ast_expression(*right)?;
|
||||
let op = match op {
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperator::Add => {
|
||||
AlgebraicBinaryOperator::Add
|
||||
}
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperator::Sub => {
|
||||
AlgebraicBinaryOperator::Sub
|
||||
}
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperator::Mul => {
|
||||
AlgebraicBinaryOperator::Mul
|
||||
}
|
||||
// Can't be represented, return an none.
|
||||
powdr_ast::analyzed::AlgebraicBinaryOperator::Pow => return None,
|
||||
};
|
||||
Some(AlgebraicExpression::new_binary(left, op, right))
|
||||
}
|
||||
powdr_ast::analyzed::AlgebraicExpression::UnaryOperation(
|
||||
powdr_ast::analyzed::AlgebraicUnaryOperation { op, expr },
|
||||
) => {
|
||||
let expr = AlgebraicExpression::try_from_ast_expression(*expr)?;
|
||||
let op = match op {
|
||||
powdr_ast::analyzed::AlgebraicUnaryOperator::Minus => {
|
||||
AlgebraicUnaryOperator::Minus
|
||||
}
|
||||
};
|
||||
Some(AlgebraicExpression::new_unary(op, expr))
|
||||
}
|
||||
// Can't be represented, return an none.
|
||||
powdr_ast::analyzed::AlgebraicExpression::PublicReference(_)
|
||||
| powdr_ast::analyzed::AlgebraicExpression::Challenge(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,35 @@
|
||||
use constraint_optimizer::IsBusStateful;
|
||||
use itertools::Itertools;
|
||||
use legacy_expression::ast_compatibility::CompatibleWithAstExpression;
|
||||
use legacy_expression::{AlgebraicExpression, AlgebraicReference, PolyID, PolynomialType};
|
||||
use powdr::UniqueColumns;
|
||||
use powdr_ast::analyzed::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicExpression, AlgebraicReference,
|
||||
AlgebraicUnaryOperation, AlgebraicUnaryOperator,
|
||||
};
|
||||
use powdr_ast::analyzed::{PolyID, PolynomialType};
|
||||
use powdr_ast::parsed::visitor::Children;
|
||||
use powdr_constraint_solver::constraint_system::BusInteractionHandler;
|
||||
use powdr_expression::{
|
||||
visitors::Children, AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicUnaryOperation,
|
||||
AlgebraicUnaryOperator,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Display;
|
||||
use std::iter::once;
|
||||
use std::{collections::BTreeMap, iter::once};
|
||||
use symbolic_machine_generator::statements_to_symbolic_machine;
|
||||
|
||||
use powdr_number::FieldElement;
|
||||
use powdr_pilopt::simplify_expression;
|
||||
|
||||
pub mod constraint_optimizer;
|
||||
pub mod legacy_expression;
|
||||
pub mod memory_optimizer;
|
||||
pub mod optimizer;
|
||||
pub mod powdr;
|
||||
mod stats_logger;
|
||||
pub mod symbolic_machine_generator;
|
||||
|
||||
pub fn simplify_expression<T: FieldElement>(e: AlgebraicExpression<T>) -> AlgebraicExpression<T> {
|
||||
// Wrap powdr_pilopt::simplify_expression, which uses powdr_ast::analyzed::AlgebraicExpression.
|
||||
let ast_expression = e.into_ast_expression();
|
||||
let ast_expression = powdr_pilopt::simplify_expression(ast_expression);
|
||||
AlgebraicExpression::try_from_ast_expression(ast_expression).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct SymbolicInstructionStatement<T> {
|
||||
pub opcode: usize,
|
||||
@@ -125,12 +131,7 @@ impl<T: Display> Display for SymbolicMachine<T> {
|
||||
|
||||
impl<T: Clone + Ord + std::fmt::Display> SymbolicMachine<T> {
|
||||
pub fn degree(&self) -> usize {
|
||||
let mut cache = Default::default();
|
||||
let ints = Default::default();
|
||||
self.children()
|
||||
.map(|e| e.degree_with_cache(&ints, &mut cache))
|
||||
.max()
|
||||
.unwrap_or(0)
|
||||
self.children().map(|e| e.degree()).max().unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +256,6 @@ fn add_guards_constraint<T: FieldElement>(
|
||||
Box::new(add_guards_constraint(*right, is_valid))
|
||||
}
|
||||
AlgebraicBinaryOperator::Mul => right,
|
||||
AlgebraicBinaryOperator::Pow => unimplemented!(),
|
||||
};
|
||||
AlgebraicExpression::new_binary(left, op, *right)
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@ use std::fmt::Display;
|
||||
use std::hash::Hash;
|
||||
|
||||
use itertools::Itertools;
|
||||
use powdr_ast::analyzed::{
|
||||
algebraic_expression_conversion, AlgebraicExpression, AlgebraicReference, Challenge,
|
||||
};
|
||||
use powdr_constraint_solver::boolean_extractor;
|
||||
use powdr_constraint_solver::constraint_system::{ConstraintRef, ConstraintSystem};
|
||||
use powdr_constraint_solver::indexed_constraint_system::IndexedConstraintSystem;
|
||||
@@ -16,6 +13,7 @@ use powdr_constraint_solver::range_constraint::RangeConstraint;
|
||||
use powdr_constraint_solver::utils::possible_concrete_values;
|
||||
use powdr_number::{FieldElement, LargeInt};
|
||||
|
||||
use crate::legacy_expression::{AlgebraicExpression, AlgebraicReference};
|
||||
use crate::{SymbolicBusInteraction, SymbolicConstraint, SymbolicMachine, MEMORY_BUS_ID};
|
||||
|
||||
/// Optimizes bus sends that correspond to general-purpose memory read and write operations.
|
||||
@@ -322,8 +320,6 @@ fn symbolic_to_simplified_constraints<T: FieldElement>(
|
||||
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Variable {
|
||||
Reference(AlgebraicReference),
|
||||
PublicReference(String),
|
||||
Challenge(Challenge),
|
||||
Boolean(usize),
|
||||
}
|
||||
|
||||
@@ -331,8 +327,6 @@ impl Display for Variable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Variable::Reference(r) => write!(f, "{r}"),
|
||||
Variable::PublicReference(r) => write!(f, "{r}"),
|
||||
Variable::Challenge(c) => write!(f, "{c}"),
|
||||
Variable::Boolean(id) => write!(f, "boolean_{id}"),
|
||||
}
|
||||
}
|
||||
@@ -403,25 +397,9 @@ fn is_value_known_to_be_different_by_word<T: FieldElement, V: Clone + Ord + Hash
|
||||
pub fn algebraic_to_quadratic_symbolic_expression<T: FieldElement>(
|
||||
expr: &AlgebraicExpression<T>,
|
||||
) -> QuadraticSymbolicExpression<T, Variable> {
|
||||
type Qse<T> = QuadraticSymbolicExpression<T, Variable>;
|
||||
|
||||
struct TerminalConverter;
|
||||
|
||||
impl<T: FieldElement> algebraic_expression_conversion::TerminalConverter<Qse<T>>
|
||||
for TerminalConverter
|
||||
{
|
||||
fn convert_reference(&mut self, reference: &AlgebraicReference) -> Qse<T> {
|
||||
Qse::from_unknown_variable(Variable::Reference(reference.clone()))
|
||||
}
|
||||
fn convert_public_reference(&mut self, reference: &str) -> Qse<T> {
|
||||
Qse::from_unknown_variable(Variable::PublicReference(reference.to_string()))
|
||||
}
|
||||
fn convert_challenge(&mut self, challenge: &Challenge) -> Qse<T> {
|
||||
Qse::from_unknown_variable(Variable::Challenge(*challenge))
|
||||
}
|
||||
}
|
||||
|
||||
algebraic_expression_conversion::convert(expr, &mut TerminalConverter)
|
||||
powdr_expression::conversion::convert(expr, &mut |reference| {
|
||||
QuadraticSymbolicExpression::from_unknown_variable(Variable::Reference(reference.clone()))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::simplify_expression;
|
||||
use itertools::Itertools;
|
||||
use powdr_ast::analyzed::AlgebraicExpression;
|
||||
use powdr_constraint_solver::{
|
||||
constraint_system::{BusInteraction, BusInteractionHandler, ConstraintSystem},
|
||||
quadratic_symbolic_expression::QuadraticSymbolicExpression,
|
||||
symbolic_expression::SymbolicExpression,
|
||||
};
|
||||
use powdr_number::FieldElement;
|
||||
use powdr_pilopt::{
|
||||
qse_opt::{
|
||||
algebraic_to_quadratic_symbolic_expression, quadratic_symbolic_expression_to_algebraic,
|
||||
Variable,
|
||||
},
|
||||
simplify_expression,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
constraint_optimizer::{optimize_constraints, IsBusStateful},
|
||||
legacy_expression::{
|
||||
ast_compatibility::CompatibleWithAstExpression, AlgebraicExpression, AlgebraicReference,
|
||||
},
|
||||
memory_optimizer::{check_register_operation_consistency, optimize_memory},
|
||||
powdr::{self},
|
||||
stats_logger::StatsLogger,
|
||||
@@ -59,11 +55,11 @@ pub fn optimize<T: FieldElement>(
|
||||
}
|
||||
|
||||
fn optimization_loop_iteration<T: FieldElement>(
|
||||
constraint_system: ConstraintSystem<T, Variable>,
|
||||
constraint_system: ConstraintSystem<T, AlgebraicReference>,
|
||||
bus_interaction_handler: impl BusInteractionHandler<T> + IsBusStateful<T> + Clone,
|
||||
degree_bound: usize,
|
||||
stats_logger: &mut StatsLogger,
|
||||
) -> Result<ConstraintSystem<T, Variable>, crate::constraint_optimizer::Error> {
|
||||
) -> Result<ConstraintSystem<T, AlgebraicReference>, crate::constraint_optimizer::Error> {
|
||||
let constraint_system = optimize_constraints(
|
||||
constraint_system,
|
||||
bus_interaction_handler.clone(),
|
||||
@@ -79,7 +75,9 @@ fn optimization_loop_iteration<T: FieldElement>(
|
||||
Ok(symbolic_machine_to_constraint_system(machine))
|
||||
}
|
||||
|
||||
fn system_size<T: FieldElement>(constraint_system: &ConstraintSystem<T, Variable>) -> [usize; 3] {
|
||||
fn system_size<T: FieldElement>(
|
||||
constraint_system: &ConstraintSystem<T, AlgebraicReference>,
|
||||
) -> [usize; 3] {
|
||||
[
|
||||
constraint_system.algebraic_constraints.len(),
|
||||
constraint_system.bus_interactions.len(),
|
||||
@@ -198,7 +196,7 @@ pub fn optimize_exec_bus<T: FieldElement>(mut machine: SymbolicMachine<T>) -> Sy
|
||||
|
||||
fn symbolic_machine_to_constraint_system<P: FieldElement>(
|
||||
symbolic_machine: SymbolicMachine<P>,
|
||||
) -> ConstraintSystem<P, Variable> {
|
||||
) -> ConstraintSystem<P, AlgebraicReference> {
|
||||
ConstraintSystem {
|
||||
algebraic_constraints: symbolic_machine
|
||||
.constraints
|
||||
@@ -214,7 +212,7 @@ fn symbolic_machine_to_constraint_system<P: FieldElement>(
|
||||
}
|
||||
|
||||
fn constraint_system_to_symbolic_machine<P: FieldElement>(
|
||||
constraint_system: ConstraintSystem<P, Variable>,
|
||||
constraint_system: ConstraintSystem<P, AlgebraicReference>,
|
||||
) -> SymbolicMachine<P> {
|
||||
SymbolicMachine {
|
||||
constraints: constraint_system
|
||||
@@ -234,7 +232,7 @@ fn constraint_system_to_symbolic_machine<P: FieldElement>(
|
||||
|
||||
fn symbolic_bus_interaction_to_bus_interaction<P: FieldElement>(
|
||||
bus_interaction: &SymbolicBusInteraction<P>,
|
||||
) -> BusInteraction<QuadraticSymbolicExpression<P, Variable>> {
|
||||
) -> BusInteraction<QuadraticSymbolicExpression<P, AlgebraicReference>> {
|
||||
BusInteraction {
|
||||
bus_id: SymbolicExpression::Concrete(P::from(bus_interaction.id)).into(),
|
||||
payload: bus_interaction
|
||||
@@ -247,7 +245,7 @@ fn symbolic_bus_interaction_to_bus_interaction<P: FieldElement>(
|
||||
}
|
||||
|
||||
fn bus_interaction_to_symbolic_bus_interaction<P: FieldElement>(
|
||||
bus_interaction: BusInteraction<QuadraticSymbolicExpression<P, Variable>>,
|
||||
bus_interaction: BusInteraction<QuadraticSymbolicExpression<P, AlgebraicReference>>,
|
||||
) -> SymbolicBusInteraction<P> {
|
||||
// We set the bus_id to a constant in `bus_interaction_to_symbolic_bus_interaction`,
|
||||
// so this should always succeed.
|
||||
@@ -270,3 +268,32 @@ fn bus_interaction_to_symbolic_bus_interaction<P: FieldElement>(
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Turns an algebraic expression into a quadratic symbolic expression,
|
||||
/// assuming all [`AlgebraicReference`]s are unknown variables.
|
||||
pub fn algebraic_to_quadratic_symbolic_expression<T: FieldElement>(
|
||||
expr: &AlgebraicExpression<T>,
|
||||
) -> QuadraticSymbolicExpression<T, AlgebraicReference> {
|
||||
powdr_expression::conversion::convert(expr, &mut |reference| {
|
||||
QuadraticSymbolicExpression::from_unknown_variable(reference.clone())
|
||||
})
|
||||
}
|
||||
|
||||
/// Turns a quadratic symbolic expression back into an algebraic expression.
|
||||
/// Tries to simplify the expression wrt negation and constant factors
|
||||
/// to aid human readability.
|
||||
pub fn quadratic_symbolic_expression_to_algebraic<T: FieldElement>(
|
||||
expr: &QuadraticSymbolicExpression<T, AlgebraicReference>,
|
||||
) -> AlgebraicExpression<T> {
|
||||
// Wrap `powdr_pilopt::qse_opt::quadratic_symbolic_expression_to_algebraic`, which
|
||||
// works on a `powdr_ast::analyzed::AlgebraicExpression`.
|
||||
let expr = expr.transform_var_type(&mut |algebraic_reference| {
|
||||
powdr_pilopt::qse_opt::Variable::Reference(algebraic_reference.clone().into())
|
||||
});
|
||||
// This is where the core conversion is implemented, including the simplification.
|
||||
let ast_algebraic_expression =
|
||||
powdr_pilopt::qse_opt::quadratic_symbolic_expression_to_algebraic(&expr);
|
||||
// Unwrap should be fine, because by construction we don't have challenges or public references,
|
||||
// and quadratic_symbolic_expression_to_algebraic should not introduce any exponentiations.
|
||||
AlgebraicExpression::try_from_ast_expression(ast_algebraic_expression).unwrap()
|
||||
}
|
||||
|
||||
@@ -2,22 +2,13 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::iter::from_fn;
|
||||
|
||||
use itertools::Itertools;
|
||||
use powdr_ast::analyzed::{
|
||||
AlgebraicBinaryOperation, AlgebraicExpression, AlgebraicReference, AlgebraicReferenceThin,
|
||||
AlgebraicUnaryOperation, PolyID, PolynomialType,
|
||||
};
|
||||
use powdr_ast::parsed::asm::SymbolPath;
|
||||
use powdr_ast::parsed::visitor::AllChildren;
|
||||
use powdr_ast::parsed::{
|
||||
visitor::ExpressionVisitable, NamespacedPolynomialReference, UnaryOperator,
|
||||
};
|
||||
use powdr_expression::visitors::{AllChildren, ExpressionVisitable};
|
||||
use powdr_number::FieldElement;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::legacy_expression::{AlgebraicExpression, AlgebraicReference, PolyID, PolynomialType};
|
||||
use crate::SymbolicMachine;
|
||||
|
||||
type Expression = powdr_ast::asm_analysis::Expression<NamespacedPolynomialReference>;
|
||||
|
||||
// After powdr and lib are adjusted, this function can be renamed and the old substitute removed
|
||||
pub fn substitute_algebraic<T: Clone>(
|
||||
expr: &mut AlgebraicExpression<T>,
|
||||
@@ -192,67 +183,3 @@ pub fn reassign_ids<T: FieldElement>(
|
||||
|
||||
(curr_id, subs, machine)
|
||||
}
|
||||
|
||||
pub fn substitute(expr: &mut Expression, sub: &BTreeMap<String, Expression>) {
|
||||
expr.pre_visit_expressions_mut(&mut |expr| match expr {
|
||||
Expression::Reference(_, ref mut r) => {
|
||||
if let Some(sub_expr) = sub.get(&r.path.to_string()) {
|
||||
*expr = sub_expr.clone();
|
||||
}
|
||||
}
|
||||
Expression::UnaryOperation(_, ref mut un_op) => {
|
||||
if matches!(un_op.op, UnaryOperator::Next) {
|
||||
if let Expression::Reference(_, ref r) = &*un_op.expr {
|
||||
let name = r.path.try_last_part().unwrap();
|
||||
if name == "pc" {
|
||||
let pc_next_symbol = SymbolPath::from_identifier("pc_next".to_string());
|
||||
let pc_next_ref: NamespacedPolynomialReference = pc_next_symbol.into();
|
||||
let pc_next_ref = Expression::Reference(Default::default(), pc_next_ref);
|
||||
*expr = pc_next_ref.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
/// Replaces any reference of intermediates with their definitions.
|
||||
/// This is needed because powdr Autoprecompiles currently does not implement
|
||||
/// intermediates.
|
||||
pub fn inline_intermediates<T: FieldElement>(
|
||||
expr: AlgebraicExpression<T>,
|
||||
intermediates: &BTreeMap<AlgebraicReferenceThin, AlgebraicExpression<T>>,
|
||||
) -> AlgebraicExpression<T> {
|
||||
match expr {
|
||||
AlgebraicExpression::Reference(ref algebraic_reference) => {
|
||||
if algebraic_reference.poly_id.ptype == PolynomialType::Intermediate {
|
||||
inline_intermediates(
|
||||
intermediates
|
||||
.get(&algebraic_reference.to_thin())
|
||||
.expect("Intermediate not found")
|
||||
.clone(),
|
||||
intermediates,
|
||||
)
|
||||
} else {
|
||||
expr
|
||||
}
|
||||
}
|
||||
AlgebraicExpression::PublicReference(..)
|
||||
| AlgebraicExpression::Challenge(..)
|
||||
| AlgebraicExpression::Number(..) => expr,
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation { left, op, right }) => {
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
||||
left: Box::new(inline_intermediates(*left, intermediates)),
|
||||
op,
|
||||
right: Box::new(inline_intermediates(*right, intermediates)),
|
||||
})
|
||||
}
|
||||
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { op, expr }) => {
|
||||
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation {
|
||||
op,
|
||||
expr: Box::new(inline_intermediates(*expr, intermediates)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@ use std::hash::Hash;
|
||||
use std::{fmt::Display, time::Instant};
|
||||
|
||||
use itertools::Itertools;
|
||||
use powdr_ast::analyzed::PolynomialType;
|
||||
use powdr_constraint_solver::constraint_system::ConstraintSystem;
|
||||
use powdr_number::FieldElement;
|
||||
use powdr_pilopt::qse_opt::Variable;
|
||||
|
||||
use crate::{powdr::UniqueColumns, SymbolicMachine};
|
||||
use crate::{
|
||||
legacy_expression::{AlgebraicReference, PolynomialType},
|
||||
powdr::UniqueColumns,
|
||||
SymbolicMachine,
|
||||
};
|
||||
|
||||
pub struct StatsLogger {
|
||||
start_time: Instant,
|
||||
@@ -82,11 +84,8 @@ pub trait IsWitnessColumn {
|
||||
fn is_witness_column(&self) -> bool;
|
||||
}
|
||||
|
||||
impl IsWitnessColumn for Variable {
|
||||
impl IsWitnessColumn for AlgebraicReference {
|
||||
fn is_witness_column(&self) -> bool {
|
||||
match self {
|
||||
Variable::Reference(poly) => poly.is_witness(),
|
||||
_ => false,
|
||||
}
|
||||
self.poly_id.ptype == PolynomialType::Committed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use super::simplify_expression;
|
||||
use itertools::Itertools;
|
||||
use powdr_ast::analyzed::{AlgebraicBinaryOperation, AlgebraicExpression};
|
||||
use powdr_expression::AlgebraicBinaryOperation;
|
||||
use powdr_number::{FieldElement, LargeInt};
|
||||
use powdr_pilopt::simplify_expression;
|
||||
|
||||
use crate::{
|
||||
legacy_expression::AlgebraicExpression,
|
||||
powdr::{self, Column},
|
||||
PcLookupBusInteraction, SymbolicBusInteraction, SymbolicConstraint,
|
||||
SymbolicInstructionStatement, SymbolicMachine, EXECUTION_BUS_ID, MEMORY_BUS_ID,
|
||||
|
||||
27
expression/Cargo.toml
Normal file
27
expression/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "powdr-expression"
|
||||
description = "powdr expression type"
|
||||
version = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
license = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
powdr-number.workspace = true
|
||||
|
||||
itertools = "0.13"
|
||||
num-traits = "0.2.15"
|
||||
derive_more = "0.99.17"
|
||||
serde = { version = "1.0", default-features = false, features = ["alloc", "derive", "rc"] }
|
||||
schemars = { version = "0.8.16", features = ["preserve_order"]}
|
||||
|
||||
[dev-dependencies]
|
||||
test-log = "0.2.12"
|
||||
pretty_assertions = "1.4.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
bench = false # See https://github.com/bheisler/criterion.rs/issues/458
|
||||
38
expression/src/conversion.rs
Normal file
38
expression/src/conversion.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use powdr_number::FieldElement;
|
||||
|
||||
use super::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicExpression,
|
||||
AlgebraicUnaryOperation, AlgebraicUnaryOperator,
|
||||
};
|
||||
|
||||
/// Converts an AlgebraicExpression into a different structure that supports algebraic operations.
|
||||
/// The `reference_converter` is used to convert the reference that appear in the expression.
|
||||
pub fn convert<T: FieldElement, R, Target>(
|
||||
expr: &AlgebraicExpression<T, R>,
|
||||
reference_converter: &mut impl FnMut(&R) -> Target,
|
||||
) -> Target
|
||||
where
|
||||
Target: From<T>
|
||||
+ Clone
|
||||
+ std::ops::Add<Output = Target>
|
||||
+ std::ops::Sub<Output = Target>
|
||||
+ std::ops::Mul<Output = Target>
|
||||
+ std::ops::Neg<Output = Target>,
|
||||
{
|
||||
match expr {
|
||||
AlgebraicExpression::Reference(r) => reference_converter(r),
|
||||
AlgebraicExpression::Number(n) => (*n).into(),
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation { left, op, right }) => {
|
||||
let left = convert(left, reference_converter);
|
||||
let right = convert(right, reference_converter);
|
||||
match op {
|
||||
AlgebraicBinaryOperator::Add => left + right,
|
||||
AlgebraicBinaryOperator::Sub => left - right,
|
||||
AlgebraicBinaryOperator::Mul => left * right,
|
||||
}
|
||||
}
|
||||
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { op, expr }) => match op {
|
||||
AlgebraicUnaryOperator::Minus => -convert(expr, reference_converter),
|
||||
},
|
||||
}
|
||||
}
|
||||
133
expression/src/display.rs
Normal file
133
expression/src/display.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicExpression,
|
||||
AlgebraicUnaryOperation, AlgebraicUnaryOperator,
|
||||
};
|
||||
|
||||
type ExpressionPrecedence = u64;
|
||||
trait Precedence {
|
||||
fn precedence(&self) -> Option<ExpressionPrecedence>;
|
||||
}
|
||||
|
||||
impl Precedence for AlgebraicUnaryOperator {
|
||||
fn precedence(&self) -> Option<ExpressionPrecedence> {
|
||||
Some(match self {
|
||||
AlgebraicUnaryOperator::Minus => 1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Precedence for AlgebraicBinaryOperator {
|
||||
fn precedence(&self) -> Option<ExpressionPrecedence> {
|
||||
Some(match self {
|
||||
Self::Mul => 3,
|
||||
Self::Add | Self::Sub => 4,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> Precedence for AlgebraicExpression<T, R> {
|
||||
fn precedence(&self) -> Option<ExpressionPrecedence> {
|
||||
match self {
|
||||
AlgebraicExpression::UnaryOperation(operation) => operation.op.precedence(),
|
||||
AlgebraicExpression::BinaryOperation(operation) => operation.op.precedence(),
|
||||
AlgebraicExpression::Number(..) | AlgebraicExpression::Reference(..) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Display, R: Display> Display for AlgebraicBinaryOperation<T, R> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let op_precedence = self.op.precedence().unwrap();
|
||||
let use_left_parentheses = match self.left.precedence() {
|
||||
Some(left_precedence) => left_precedence > op_precedence,
|
||||
None => false,
|
||||
};
|
||||
|
||||
let use_right_parentheses = match self.right.precedence() {
|
||||
Some(right_precedence) => right_precedence >= op_precedence,
|
||||
None => false,
|
||||
};
|
||||
|
||||
let left_string = if use_left_parentheses {
|
||||
format!("({})", self.left)
|
||||
} else {
|
||||
format!("{}", self.left)
|
||||
};
|
||||
let right_string = if use_right_parentheses {
|
||||
format!("({})", self.right)
|
||||
} else {
|
||||
format!("{}", self.right)
|
||||
};
|
||||
|
||||
write!(f, "{left_string} {} {right_string}", self.op)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Display, R: Display> Display for AlgebraicUnaryOperation<T, R> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let exp_string = match (self.op.precedence(), self.expr.precedence()) {
|
||||
(Some(precedence), Some(inner_precedence)) if precedence < inner_precedence => {
|
||||
format!("({})", self.expr)
|
||||
}
|
||||
_ => {
|
||||
format!("{}", self.expr)
|
||||
}
|
||||
};
|
||||
|
||||
write!(f, "{}{exp_string}", self.op)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AlgebraicUnaryOperator {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AlgebraicUnaryOperator::Minus => write!(f, "-"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AlgebraicBinaryOperator {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
AlgebraicBinaryOperator::Add => write!(f, "+"),
|
||||
AlgebraicBinaryOperator::Sub => write!(f, "-"),
|
||||
AlgebraicBinaryOperator::Mul => write!(f, "*"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use powdr_number::GoldilocksField;
|
||||
use pretty_assertions::assert_eq;
|
||||
use test_log::test;
|
||||
|
||||
use super::AlgebraicExpression;
|
||||
|
||||
fn test_display(expr: AlgebraicExpression<GoldilocksField, &str>, expected: &str) {
|
||||
assert_eq!(expr.to_string(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binary_op() {
|
||||
let x = AlgebraicExpression::Reference("x");
|
||||
let y = AlgebraicExpression::Reference("y");
|
||||
let z = AlgebraicExpression::Reference("z");
|
||||
// Don't add extra
|
||||
test_display(x.clone() + y.clone() + z.clone(), "x + y + z");
|
||||
test_display(x.clone() * y.clone() * z.clone(), "x * y * z");
|
||||
// Remove unneeded
|
||||
test_display(-x.clone() + y.clone() * z.clone(), "-x + y * z");
|
||||
test_display((x.clone() * y.clone()) * z.clone(), "x * y * z");
|
||||
test_display(x.clone() - (y.clone() + z.clone()), "x - (y + z)");
|
||||
// Observe associativity
|
||||
test_display(x.clone() * (y.clone() * z.clone()), "x * (y * z)");
|
||||
test_display(x.clone() + (y.clone() + z.clone()), "x + (y + z)");
|
||||
// Don't remove needed
|
||||
test_display((x.clone() + y.clone()) * z.clone(), "(x + y) * z");
|
||||
test_display((x.clone() + y.clone()) * z.clone(), "(x + y) * z");
|
||||
test_display(-(x.clone() + y.clone()), "-(x + y)");
|
||||
}
|
||||
}
|
||||
166
expression/src/lib.rs
Normal file
166
expression/src/lib.rs
Normal file
@@ -0,0 +1,166 @@
|
||||
use std::{iter, ops};
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod conversion;
|
||||
pub mod display;
|
||||
pub mod visitors;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
JsonSchema,
|
||||
Hash,
|
||||
derive_more::Display,
|
||||
)]
|
||||
pub enum AlgebraicExpression<T, R> {
|
||||
Reference(R),
|
||||
Number(T),
|
||||
BinaryOperation(AlgebraicBinaryOperation<T, R>),
|
||||
UnaryOperation(AlgebraicUnaryOperation<T, R>),
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, JsonSchema, Hash,
|
||||
)]
|
||||
pub struct AlgebraicBinaryOperation<T, R> {
|
||||
pub left: Box<AlgebraicExpression<T, R>>,
|
||||
pub op: AlgebraicBinaryOperator,
|
||||
pub right: Box<AlgebraicExpression<T, R>>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Deserialize, JsonSchema, Hash,
|
||||
)]
|
||||
pub enum AlgebraicBinaryOperator {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, JsonSchema, Hash,
|
||||
)]
|
||||
pub struct AlgebraicUnaryOperation<T, R> {
|
||||
pub op: AlgebraicUnaryOperator,
|
||||
pub expr: Box<AlgebraicExpression<T, R>>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Deserialize, JsonSchema, Hash,
|
||||
)]
|
||||
pub enum AlgebraicUnaryOperator {
|
||||
Minus,
|
||||
}
|
||||
|
||||
impl<T, R> AlgebraicExpression<T, R> {
|
||||
/// Returns an iterator over all (top-level) expressions in this expression.
|
||||
/// This specifically does not implement the Children trait because otherwise it
|
||||
/// would have a wrong implementation of ExpressionVisitable (which is implemented
|
||||
/// generically for all types that implement Children<Expr>).
|
||||
fn children(&self) -> Box<dyn Iterator<Item = &AlgebraicExpression<T, R>> + '_> {
|
||||
match self {
|
||||
AlgebraicExpression::Reference(_) | AlgebraicExpression::Number(_) => {
|
||||
Box::new(iter::empty())
|
||||
}
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
||||
left, right, ..
|
||||
}) => Box::new([left.as_ref(), right.as_ref()].into_iter()),
|
||||
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { expr: e, .. }) => {
|
||||
Box::new([e.as_ref()].into_iter())
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Returns an iterator over all (top-level) expressions in this expression.
|
||||
/// This specifically does not implement the Children trait because otherwise it
|
||||
/// would have a wrong implementation of ExpressionVisitable (which is implemented
|
||||
/// generically for all types that implement Children<Expr>).
|
||||
fn children_mut(&mut self) -> Box<dyn Iterator<Item = &mut AlgebraicExpression<T, R>> + '_> {
|
||||
match self {
|
||||
AlgebraicExpression::Reference(_) | AlgebraicExpression::Number(_) => {
|
||||
Box::new(iter::empty())
|
||||
}
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
||||
left, right, ..
|
||||
}) => Box::new([left.as_mut(), right.as_mut()].into_iter()),
|
||||
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation { expr: e, .. }) => {
|
||||
Box::new([e.as_mut()].into_iter())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the degree of the expressions
|
||||
pub fn degree(&self) -> usize {
|
||||
match self {
|
||||
AlgebraicExpression::Reference(..) => 1,
|
||||
// Multiplying two expressions adds their degrees
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
||||
op: AlgebraicBinaryOperator::Mul,
|
||||
left,
|
||||
right,
|
||||
}) => left.degree() + right.degree(),
|
||||
// In all other cases, we take the maximum of the degrees of the children
|
||||
_ => self.children().map(|e| e.degree()).max().unwrap_or(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_binary(left: Self, op: AlgebraicBinaryOperator, right: Self) -> Self {
|
||||
AlgebraicExpression::BinaryOperation(AlgebraicBinaryOperation {
|
||||
left: Box::new(left),
|
||||
op,
|
||||
right: Box::new(right),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_unary(op: AlgebraicUnaryOperator, expr: Self) -> Self {
|
||||
AlgebraicExpression::UnaryOperation(AlgebraicUnaryOperation {
|
||||
op,
|
||||
expr: Box::new(expr),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> ops::Add for AlgebraicExpression<T, R> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self::new_binary(self, AlgebraicBinaryOperator::Add, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> ops::Sub for AlgebraicExpression<T, R> {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Self::new_binary(self, AlgebraicBinaryOperator::Sub, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> ops::Neg for AlgebraicExpression<T, R> {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
Self::new_unary(AlgebraicUnaryOperator::Minus, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> ops::Mul for AlgebraicExpression<T, R> {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Self::new_binary(self, AlgebraicBinaryOperator::Mul, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> From<T> for AlgebraicExpression<T, R> {
|
||||
fn from(value: T) -> Self {
|
||||
AlgebraicExpression::Number(value)
|
||||
}
|
||||
}
|
||||
136
expression/src/visitors.rs
Normal file
136
expression/src/visitors.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use std::{iter, ops::ControlFlow};
|
||||
|
||||
use crate::AlgebraicExpression;
|
||||
|
||||
/// Generic trait that allows to iterate over sub-structures.
|
||||
///
|
||||
/// It is only meant to iterate non-recursively over the direct children.
|
||||
/// Self and O do not have to be the same type and we can also have
|
||||
/// Children<O1> and Children<O2> implemented for the same type,
|
||||
/// if the goal is to iterate over sub-structures of different kinds.
|
||||
pub trait Children<O> {
|
||||
/// Returns an iterator over all direct children of kind O in this object.
|
||||
fn children(&self) -> Box<dyn Iterator<Item = &O> + '_>;
|
||||
/// Returns an iterator over all direct children of kind Q in this object.
|
||||
fn children_mut(&mut self) -> Box<dyn Iterator<Item = &mut O> + '_>;
|
||||
}
|
||||
|
||||
pub trait AllChildren<O> {
|
||||
/// Returns an iterator over all direct and indirect children of kind `O` in this object.
|
||||
/// If `O` and `Self` are the same type, also includes `self`.
|
||||
/// Pre-order visitor.
|
||||
fn all_children(&self) -> Box<dyn Iterator<Item = &O> + '_>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum VisitOrder {
|
||||
Pre,
|
||||
Post,
|
||||
}
|
||||
|
||||
/// A trait to be implemented by an AST node.
|
||||
///
|
||||
/// The idea is that it calls a callback function on each of the sub-nodes
|
||||
/// that are expressions.
|
||||
/// The difference to the Children<Expr> trait is that ExpressionVisitable
|
||||
/// visits recursively.
|
||||
/// If a node implements Children<Expr>, it also implements ExpressionVisitable<Expr>.
|
||||
pub trait ExpressionVisitable<Expr> {
|
||||
/// Traverses the AST and calls `f` on each Expression in pre-order.
|
||||
fn pre_visit_expressions_mut<F>(&mut self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&mut Expr),
|
||||
{
|
||||
let _ = self.visit_expressions_mut(
|
||||
&mut move |e| {
|
||||
f(e);
|
||||
ControlFlow::Continue::<()>(())
|
||||
},
|
||||
VisitOrder::Pre,
|
||||
);
|
||||
}
|
||||
|
||||
/// Traverses the AST and calls `f` on each Expression in post-order.
|
||||
fn post_visit_expressions_mut<F>(&mut self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&mut Expr),
|
||||
{
|
||||
let _ = self.visit_expressions_mut(
|
||||
&mut move |e| {
|
||||
f(e);
|
||||
ControlFlow::Continue::<()>(())
|
||||
},
|
||||
VisitOrder::Post,
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_expressions<F, B>(&self, f: &mut F, order: VisitOrder) -> ControlFlow<B>
|
||||
where
|
||||
F: FnMut(&Expr) -> ControlFlow<B>;
|
||||
|
||||
fn visit_expressions_mut<F, B>(&mut self, f: &mut F, order: VisitOrder) -> ControlFlow<B>
|
||||
where
|
||||
F: FnMut(&mut Expr) -> ControlFlow<B>;
|
||||
}
|
||||
|
||||
impl<Expr: ExpressionVisitable<Expr>, C: Children<Expr>> ExpressionVisitable<Expr> for C {
|
||||
fn visit_expressions_mut<F, B>(&mut self, f: &mut F, o: VisitOrder) -> ControlFlow<B>
|
||||
where
|
||||
F: FnMut(&mut Expr) -> ControlFlow<B>,
|
||||
{
|
||||
self.children_mut()
|
||||
.try_for_each(|child| child.visit_expressions_mut(f, o))
|
||||
}
|
||||
|
||||
fn visit_expressions<F, B>(&self, f: &mut F, o: VisitOrder) -> ControlFlow<B>
|
||||
where
|
||||
F: FnMut(&Expr) -> ControlFlow<B>,
|
||||
{
|
||||
self.children()
|
||||
.try_for_each(|child| child.visit_expressions(f, o))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Expr: AllChildren<Expr>, C: Children<Expr>> AllChildren<Expr> for C {
|
||||
fn all_children(&self) -> Box<dyn Iterator<Item = &Expr> + '_> {
|
||||
Box::new(self.children().flat_map(|e| e.all_children()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> ExpressionVisitable<AlgebraicExpression<T, R>> for AlgebraicExpression<T, R> {
|
||||
fn visit_expressions_mut<F, B>(&mut self, f: &mut F, o: VisitOrder) -> ControlFlow<B>
|
||||
where
|
||||
F: FnMut(&mut AlgebraicExpression<T, R>) -> ControlFlow<B>,
|
||||
{
|
||||
if o == VisitOrder::Pre {
|
||||
f(self)?;
|
||||
}
|
||||
self.children_mut()
|
||||
.try_for_each(|e| e.visit_expressions_mut(f, o))?;
|
||||
if o == VisitOrder::Post {
|
||||
f(self)?;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
|
||||
fn visit_expressions<F, B>(&self, f: &mut F, o: VisitOrder) -> ControlFlow<B>
|
||||
where
|
||||
F: FnMut(&AlgebraicExpression<T, R>) -> ControlFlow<B>,
|
||||
{
|
||||
if o == VisitOrder::Pre {
|
||||
f(self)?;
|
||||
}
|
||||
self.children()
|
||||
.try_for_each(|e| e.visit_expressions(f, o))?;
|
||||
if o == VisitOrder::Post {
|
||||
f(self)?;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> AllChildren<AlgebraicExpression<T, R>> for AlgebraicExpression<T, R> {
|
||||
fn all_children(&self) -> Box<dyn Iterator<Item = &AlgebraicExpression<T, R>> + '_> {
|
||||
Box::new(iter::once(self).chain(self.children().flat_map(|e| e.all_children())))
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ openvm-stark-backend = { git = "https://github.com/powdr-labs/stark-backend.git"
|
||||
"jemalloc",
|
||||
] }
|
||||
|
||||
powdr-ast.workspace = true
|
||||
powdr-expression.workspace = true
|
||||
powdr-number.workspace = true
|
||||
powdr-riscv-elf.workspace = true
|
||||
powdr-autoprecompiles.workspace = true
|
||||
|
||||
@@ -3,11 +3,12 @@ use std::collections::BTreeMap;
|
||||
use super::{Gate, PlonkCircuit, Variable};
|
||||
use crate::plonk::bus_interaction_handler::add_bus_to_plonk_circuit;
|
||||
use crate::BusMap;
|
||||
use powdr_ast::analyzed::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicExpression, AlgebraicReference,
|
||||
AlgebraicUnaryOperation, AlgebraicUnaryOperator,
|
||||
};
|
||||
use powdr_autoprecompiles::legacy_expression::{AlgebraicExpression, AlgebraicReference};
|
||||
use powdr_autoprecompiles::SymbolicMachine;
|
||||
use powdr_expression::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicUnaryOperation,
|
||||
AlgebraicUnaryOperator,
|
||||
};
|
||||
use powdr_number::FieldElement;
|
||||
|
||||
pub fn build_circuit<T>(
|
||||
@@ -163,7 +164,6 @@ where
|
||||
b = self.evaluate_expression(right, false);
|
||||
}
|
||||
},
|
||||
AlgebraicBinaryOperator::Pow => unimplemented!(),
|
||||
};
|
||||
self.plonk_circuit.add_gate(Gate {
|
||||
q_l,
|
||||
@@ -193,9 +193,6 @@ where
|
||||
c
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("Unsupported algebraic expression: {algebraic_expr:?}");
|
||||
}
|
||||
};
|
||||
|
||||
self.cache.insert(algebraic_expr.clone(), result.clone());
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{bus_interaction_handler, BusMap};
|
||||
use bus_interaction_handler::BusType::{
|
||||
BitwiseLookup, ExecutionBridge, Memory, PcLookup, TupleRangeChecker, VariableRangeChecker,
|
||||
};
|
||||
use powdr_ast::analyzed::AlgebraicReference;
|
||||
use powdr_autoprecompiles::legacy_expression::AlgebraicReference;
|
||||
use powdr_autoprecompiles::SymbolicBusInteraction;
|
||||
use powdr_number::FieldElement;
|
||||
|
||||
@@ -76,7 +76,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::bus_interaction_handler::DEFAULT_MEMORY;
|
||||
use crate::plonk::test_utils::{c, var};
|
||||
use powdr_ast::analyzed::AlgebraicExpression;
|
||||
use powdr_autoprecompiles::legacy_expression::AlgebraicExpression;
|
||||
use powdr_autoprecompiles::SymbolicBusInteraction;
|
||||
use powdr_number::BabyBearField;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -234,8 +234,9 @@ impl<T, V> PlonkCircuit<T, V> {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_utils {
|
||||
use powdr_ast::analyzed::{AlgebraicExpression, AlgebraicReference};
|
||||
use powdr_ast::analyzed::{PolyID, PolynomialType};
|
||||
use powdr_autoprecompiles::legacy_expression::{
|
||||
AlgebraicExpression, AlgebraicReference, PolyID, PolynomialType,
|
||||
};
|
||||
use powdr_number::BabyBearField;
|
||||
pub fn var(name: &str, id: u64) -> AlgebraicExpression<BabyBearField> {
|
||||
AlgebraicExpression::Reference(AlgebraicReference {
|
||||
|
||||
@@ -39,8 +39,10 @@ use openvm_stark_backend::{
|
||||
rap::{AnyRap, BaseAirWithPublicValues, PartitionedBaseAir},
|
||||
Chip, ChipUsageGetter,
|
||||
};
|
||||
use powdr_ast::analyzed::AlgebraicExpression;
|
||||
use powdr_autoprecompiles::powdr::{Column, UniqueColumns};
|
||||
use powdr_autoprecompiles::{
|
||||
legacy_expression::AlgebraicExpression,
|
||||
powdr::{Column, UniqueColumns},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub struct PowdrChip<P: IntoOpenVm> {
|
||||
|
||||
@@ -29,7 +29,7 @@ use openvm_stark_backend::{
|
||||
rap::AnyRap,
|
||||
Chip, ChipUsageGetter,
|
||||
};
|
||||
use powdr_ast::analyzed::AlgebraicReference;
|
||||
use powdr_autoprecompiles::legacy_expression::AlgebraicReference;
|
||||
use powdr_autoprecompiles::powdr::UniqueColumns;
|
||||
use powdr_autoprecompiles::SymbolicMachine;
|
||||
|
||||
|
||||
@@ -11,11 +11,14 @@ use openvm_stark_backend::{
|
||||
SymbolicConstraints,
|
||||
},
|
||||
interaction::Interaction,
|
||||
p3_field::{FieldAlgebra, PrimeField32},
|
||||
p3_field::PrimeField32,
|
||||
};
|
||||
use powdr_ast::analyzed::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicExpression, AlgebraicReference,
|
||||
AlgebraicUnaryOperation, AlgebraicUnaryOperator, PolyID, PolynomialType,
|
||||
use powdr_autoprecompiles::legacy_expression::{
|
||||
AlgebraicExpression, AlgebraicReference, PolyID, PolynomialType,
|
||||
};
|
||||
use powdr_expression::{
|
||||
AlgebraicBinaryOperation, AlgebraicBinaryOperator, AlgebraicUnaryOperation,
|
||||
AlgebraicUnaryOperator,
|
||||
};
|
||||
use powdr_number::{BabyBearField, FieldElement};
|
||||
|
||||
@@ -40,31 +43,6 @@ pub fn algebraic_to_symbolic<P: IntoOpenVm>(
|
||||
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 == P::ZERO {
|
||||
SymbolicExpression::Constant(OpenVmField::<P>::ONE)
|
||||
} else {
|
||||
let mut result = base.clone();
|
||||
let mut remaining = exp - P::ONE;
|
||||
|
||||
while remaining != P::ZERO {
|
||||
result = SymbolicExpression::Mul {
|
||||
x: Arc::new(result),
|
||||
y: Arc::new(base.clone()),
|
||||
degree_multiple: 0,
|
||||
};
|
||||
remaining -= P::ONE;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
},
|
||||
AlgebraicExpression::UnaryOperation(unary) => match unary.op {
|
||||
AlgebraicUnaryOperator::Minus => SymbolicExpression::Neg {
|
||||
@@ -90,13 +68,6 @@ pub fn algebraic_to_symbolic<P: IntoOpenVm>(
|
||||
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>(
|
||||
|
||||
Reference in New Issue
Block a user