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:
Georg Wiese
2025-06-06 14:59:15 +02:00
committed by GitHub
parent 8fb46f2e44
commit 0e04662254
21 changed files with 828 additions and 194 deletions

View File

@@ -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" }

View File

@@ -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

View 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,
}
}
}
}

View File

@@ -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)
}

View File

@@ -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)]

View File

@@ -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()
}

View File

@@ -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)),
})
}
}
}

View File

@@ -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
}
}

View File

@@ -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
View 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

View 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
View 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
View 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
View 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())))
}
}

View File

@@ -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

View File

@@ -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());

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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> {

View File

@@ -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;

View File

@@ -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>(