diff --git a/Cargo.toml b/Cargo.toml index 086e72133..eb5109ced 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/autoprecompiles/Cargo.toml b/autoprecompiles/Cargo.toml index fc7359b7e..23709b906 100644 --- a/autoprecompiles/Cargo.toml +++ b/autoprecompiles/Cargo.toml @@ -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 diff --git a/autoprecompiles/src/legacy_expression.rs b/autoprecompiles/src/legacy_expression.rs new file mode 100644 index 000000000..0a1ad3888 --- /dev/null +++ b/autoprecompiles/src/legacy_expression.rs @@ -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 = powdr_expression::AlgebraicExpression; + +#[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(&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 { + 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(&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 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 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: Sized { + /// Converts the expression into an AST expression. + fn into_ast_expression(self) -> powdr_ast::analyzed::AlgebraicExpression; + + /// 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, + ) -> Option; + } + + impl CompatibleWithAstExpression for AlgebraicExpression { + fn into_ast_expression(self) -> powdr_ast::analyzed::AlgebraicExpression { + 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, + ) -> Option { + 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, + } + } + } +} diff --git a/autoprecompiles/src/lib.rs b/autoprecompiles/src/lib.rs index ede3215db..3dcc2ca36 100644 --- a/autoprecompiles/src/lib.rs +++ b/autoprecompiles/src/lib.rs @@ -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(e: AlgebraicExpression) -> AlgebraicExpression { + // 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 { pub opcode: usize, @@ -125,12 +131,7 @@ impl Display for SymbolicMachine { impl SymbolicMachine { 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( Box::new(add_guards_constraint(*right, is_valid)) } AlgebraicBinaryOperator::Mul => right, - AlgebraicBinaryOperator::Pow => unimplemented!(), }; AlgebraicExpression::new_binary(left, op, *right) } diff --git a/autoprecompiles/src/memory_optimizer.rs b/autoprecompiles/src/memory_optimizer.rs index 22485672f..a7becef61 100644 --- a/autoprecompiles/src/memory_optimizer.rs +++ b/autoprecompiles/src/memory_optimizer.rs @@ -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( #[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( expr: &AlgebraicExpression, ) -> QuadraticSymbolicExpression { - type Qse = QuadraticSymbolicExpression; - - struct TerminalConverter; - - impl algebraic_expression_conversion::TerminalConverter> - for TerminalConverter - { - fn convert_reference(&mut self, reference: &AlgebraicReference) -> Qse { - Qse::from_unknown_variable(Variable::Reference(reference.clone())) - } - fn convert_public_reference(&mut self, reference: &str) -> Qse { - Qse::from_unknown_variable(Variable::PublicReference(reference.to_string())) - } - fn convert_challenge(&mut self, challenge: &Challenge) -> Qse { - 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)] diff --git a/autoprecompiles/src/optimizer.rs b/autoprecompiles/src/optimizer.rs index b8b40d8be..baaf55147 100644 --- a/autoprecompiles/src/optimizer.rs +++ b/autoprecompiles/src/optimizer.rs @@ -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( } fn optimization_loop_iteration( - constraint_system: ConstraintSystem, + constraint_system: ConstraintSystem, bus_interaction_handler: impl BusInteractionHandler + IsBusStateful + Clone, degree_bound: usize, stats_logger: &mut StatsLogger, -) -> Result, crate::constraint_optimizer::Error> { +) -> Result, crate::constraint_optimizer::Error> { let constraint_system = optimize_constraints( constraint_system, bus_interaction_handler.clone(), @@ -79,7 +75,9 @@ fn optimization_loop_iteration( Ok(symbolic_machine_to_constraint_system(machine)) } -fn system_size(constraint_system: &ConstraintSystem) -> [usize; 3] { +fn system_size( + constraint_system: &ConstraintSystem, +) -> [usize; 3] { [ constraint_system.algebraic_constraints.len(), constraint_system.bus_interactions.len(), @@ -198,7 +196,7 @@ pub fn optimize_exec_bus(mut machine: SymbolicMachine) -> Sy fn symbolic_machine_to_constraint_system( symbolic_machine: SymbolicMachine

, -) -> ConstraintSystem { +) -> ConstraintSystem { ConstraintSystem { algebraic_constraints: symbolic_machine .constraints @@ -214,7 +212,7 @@ fn symbolic_machine_to_constraint_system( } fn constraint_system_to_symbolic_machine( - constraint_system: ConstraintSystem, + constraint_system: ConstraintSystem, ) -> SymbolicMachine

{ SymbolicMachine { constraints: constraint_system @@ -234,7 +232,7 @@ fn constraint_system_to_symbolic_machine( fn symbolic_bus_interaction_to_bus_interaction( bus_interaction: &SymbolicBusInteraction

, -) -> BusInteraction> { +) -> BusInteraction> { 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( } fn bus_interaction_to_symbolic_bus_interaction( - bus_interaction: BusInteraction>, + bus_interaction: BusInteraction>, ) -> SymbolicBusInteraction

{ // 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( )), } } + +/// Turns an algebraic expression into a quadratic symbolic expression, +/// assuming all [`AlgebraicReference`]s are unknown variables. +pub fn algebraic_to_quadratic_symbolic_expression( + expr: &AlgebraicExpression, +) -> QuadraticSymbolicExpression { + 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( + expr: &QuadraticSymbolicExpression, +) -> AlgebraicExpression { + // 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() +} diff --git a/autoprecompiles/src/powdr.rs b/autoprecompiles/src/powdr.rs index 8eb919fdf..8cb7f169a 100644 --- a/autoprecompiles/src/powdr.rs +++ b/autoprecompiles/src/powdr.rs @@ -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; - // After powdr and lib are adjusted, this function can be renamed and the old substitute removed pub fn substitute_algebraic( expr: &mut AlgebraicExpression, @@ -192,67 +183,3 @@ pub fn reassign_ids( (curr_id, subs, machine) } - -pub fn substitute(expr: &mut Expression, sub: &BTreeMap) { - 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( - expr: AlgebraicExpression, - intermediates: &BTreeMap>, -) -> AlgebraicExpression { - 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)), - }) - } - } -} diff --git a/autoprecompiles/src/stats_logger.rs b/autoprecompiles/src/stats_logger.rs index d5a213247..1a79c92f7 100644 --- a/autoprecompiles/src/stats_logger.rs +++ b/autoprecompiles/src/stats_logger.rs @@ -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 } } diff --git a/autoprecompiles/src/symbolic_machine_generator.rs b/autoprecompiles/src/symbolic_machine_generator.rs index b2dae9dcd..3bb5d64c1 100644 --- a/autoprecompiles/src/symbolic_machine_generator.rs +++ b/autoprecompiles/src/symbolic_machine_generator.rs @@ -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, diff --git a/expression/Cargo.toml b/expression/Cargo.toml new file mode 100644 index 000000000..895429c44 --- /dev/null +++ b/expression/Cargo.toml @@ -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 diff --git a/expression/src/conversion.rs b/expression/src/conversion.rs new file mode 100644 index 000000000..cbb1fe11a --- /dev/null +++ b/expression/src/conversion.rs @@ -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( + expr: &AlgebraicExpression, + reference_converter: &mut impl FnMut(&R) -> Target, +) -> Target +where + Target: From + + Clone + + std::ops::Add + + std::ops::Sub + + std::ops::Mul + + std::ops::Neg, +{ + 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), + }, + } +} diff --git a/expression/src/display.rs b/expression/src/display.rs new file mode 100644 index 000000000..3b952cf2c --- /dev/null +++ b/expression/src/display.rs @@ -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; +} + +impl Precedence for AlgebraicUnaryOperator { + fn precedence(&self) -> Option { + Some(match self { + AlgebraicUnaryOperator::Minus => 1, + }) + } +} + +impl Precedence for AlgebraicBinaryOperator { + fn precedence(&self) -> Option { + Some(match self { + Self::Mul => 3, + Self::Add | Self::Sub => 4, + }) + } +} + +impl Precedence for AlgebraicExpression { + fn precedence(&self) -> Option { + match self { + AlgebraicExpression::UnaryOperation(operation) => operation.op.precedence(), + AlgebraicExpression::BinaryOperation(operation) => operation.op.precedence(), + AlgebraicExpression::Number(..) | AlgebraicExpression::Reference(..) => None, + } + } +} + +impl Display for AlgebraicBinaryOperation { + 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 Display for AlgebraicUnaryOperation { + 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, 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)"); + } +} diff --git a/expression/src/lib.rs b/expression/src/lib.rs new file mode 100644 index 000000000..9b094243c --- /dev/null +++ b/expression/src/lib.rs @@ -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 { + Reference(R), + Number(T), + BinaryOperation(AlgebraicBinaryOperation), + UnaryOperation(AlgebraicUnaryOperation), +} + +#[derive( + Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize, JsonSchema, Hash, +)] +pub struct AlgebraicBinaryOperation { + pub left: Box>, + pub op: AlgebraicBinaryOperator, + pub right: Box>, +} + +#[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 { + pub op: AlgebraicUnaryOperator, + pub expr: Box>, +} + +#[derive( + Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Deserialize, JsonSchema, Hash, +)] +pub enum AlgebraicUnaryOperator { + Minus, +} + +impl AlgebraicExpression { + /// 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). + fn children(&self) -> Box> + '_> { + 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). + fn children_mut(&mut self) -> Box> + '_> { + 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 ops::Add for AlgebraicExpression { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self::new_binary(self, AlgebraicBinaryOperator::Add, rhs) + } +} + +impl ops::Sub for AlgebraicExpression { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self::new_binary(self, AlgebraicBinaryOperator::Sub, rhs) + } +} + +impl ops::Neg for AlgebraicExpression { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::new_unary(AlgebraicUnaryOperator::Minus, self) + } +} + +impl ops::Mul for AlgebraicExpression { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self::new_binary(self, AlgebraicBinaryOperator::Mul, rhs) + } +} + +impl From for AlgebraicExpression { + fn from(value: T) -> Self { + AlgebraicExpression::Number(value) + } +} diff --git a/expression/src/visitors.rs b/expression/src/visitors.rs new file mode 100644 index 000000000..4379f32a0 --- /dev/null +++ b/expression/src/visitors.rs @@ -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 and Children implemented for the same type, +/// if the goal is to iterate over sub-structures of different kinds. +pub trait Children { + /// Returns an iterator over all direct children of kind O in this object. + fn children(&self) -> Box + '_>; + /// Returns an iterator over all direct children of kind Q in this object. + fn children_mut(&mut self) -> Box + '_>; +} + +pub trait AllChildren { + /// 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 + '_>; +} + +#[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 trait is that ExpressionVisitable +/// visits recursively. +/// If a node implements Children, it also implements ExpressionVisitable. +pub trait ExpressionVisitable { + /// Traverses the AST and calls `f` on each Expression in pre-order. + fn pre_visit_expressions_mut(&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(&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(&self, f: &mut F, order: VisitOrder) -> ControlFlow + where + F: FnMut(&Expr) -> ControlFlow; + + fn visit_expressions_mut(&mut self, f: &mut F, order: VisitOrder) -> ControlFlow + where + F: FnMut(&mut Expr) -> ControlFlow; +} + +impl, C: Children> ExpressionVisitable for C { + fn visit_expressions_mut(&mut self, f: &mut F, o: VisitOrder) -> ControlFlow + where + F: FnMut(&mut Expr) -> ControlFlow, + { + self.children_mut() + .try_for_each(|child| child.visit_expressions_mut(f, o)) + } + + fn visit_expressions(&self, f: &mut F, o: VisitOrder) -> ControlFlow + where + F: FnMut(&Expr) -> ControlFlow, + { + self.children() + .try_for_each(|child| child.visit_expressions(f, o)) + } +} + +impl, C: Children> AllChildren for C { + fn all_children(&self) -> Box + '_> { + Box::new(self.children().flat_map(|e| e.all_children())) + } +} + +impl ExpressionVisitable> for AlgebraicExpression { + fn visit_expressions_mut(&mut self, f: &mut F, o: VisitOrder) -> ControlFlow + where + F: FnMut(&mut AlgebraicExpression) -> ControlFlow, + { + 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(&self, f: &mut F, o: VisitOrder) -> ControlFlow + where + F: FnMut(&AlgebraicExpression) -> ControlFlow, + { + 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 AllChildren> for AlgebraicExpression { + fn all_children(&self) -> Box> + '_> { + Box::new(iter::once(self).chain(self.children().flat_map(|e| e.all_children()))) + } +} diff --git a/openvm/Cargo.toml b/openvm/Cargo.toml index 60c4b4f5f..0c1410ab7 100644 --- a/openvm/Cargo.toml +++ b/openvm/Cargo.toml @@ -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 diff --git a/openvm/src/plonk/air_to_plonkish.rs b/openvm/src/plonk/air_to_plonkish.rs index 3886b31ab..15e9faa00 100644 --- a/openvm/src/plonk/air_to_plonkish.rs +++ b/openvm/src/plonk/air_to_plonkish.rs @@ -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( @@ -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()); diff --git a/openvm/src/plonk/bus_interaction_handler.rs b/openvm/src/plonk/bus_interaction_handler.rs index 4cccd1991..289acc2eb 100644 --- a/openvm/src/plonk/bus_interaction_handler.rs +++ b/openvm/src/plonk/bus_interaction_handler.rs @@ -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; diff --git a/openvm/src/plonk/mod.rs b/openvm/src/plonk/mod.rs index c37337dfc..857522bd8 100644 --- a/openvm/src/plonk/mod.rs +++ b/openvm/src/plonk/mod.rs @@ -234,8 +234,9 @@ impl PlonkCircuit { #[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 { AlgebraicExpression::Reference(AlgebraicReference { diff --git a/openvm/src/powdr_extension/chip.rs b/openvm/src/powdr_extension/chip.rs index 62a700927..6505bd461 100644 --- a/openvm/src/powdr_extension/chip.rs +++ b/openvm/src/powdr_extension/chip.rs @@ -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 { diff --git a/openvm/src/powdr_extension/plonk/chip.rs b/openvm/src/powdr_extension/plonk/chip.rs index b29563989..8e903c4cb 100644 --- a/openvm/src/powdr_extension/plonk/chip.rs +++ b/openvm/src/powdr_extension/plonk/chip.rs @@ -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; diff --git a/openvm/src/utils.rs b/openvm/src/utils.rs index c8ca36df1..c95dcd310 100644 --- a/openvm/src/utils.rs +++ b/openvm/src/utils.rs @@ -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( 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::

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