diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index f73417a4b5b..8a0c4692282 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -217,8 +217,10 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::PedersenCommitment { inputs, .. } | BlackBoxFuncCall::PedersenHash { inputs, .. } | BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. } - | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } - | BlackBoxFuncCall::Sha256Compression { inputs, .. } => inputs.to_vec(), + | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } => inputs.to_vec(), + BlackBoxFuncCall::Sha256Compression { inputs, hash_values, .. } => { + inputs.iter().chain(hash_values).copied().collect() + } BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => { vec![*lhs, *rhs] } diff --git a/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs b/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs new file mode 100644 index 00000000000..0e7d28104da --- /dev/null +++ b/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs @@ -0,0 +1,331 @@ +use std::collections::{BTreeMap, BTreeSet, HashMap}; + +use crate::{ + compiler::optimizers::GeneralOptimizer, + pwg::{ + arithmetic::ExpressionSolver, blackbox::solve_range_opcode, directives::solve_directives, + BrilligSolver, BrilligSolverStatus, + }, +}; +use acir::{ + circuit::{ + brillig::{Brillig, BrilligInputs, BrilligOutputs}, + directives::Directive, + opcodes::BlackBoxFuncCall, + Circuit, Opcode, + }, + native_types::{Expression, Witness, WitnessMap}, +}; +use acvm_blackbox_solver::StubbedBlackBoxSolver; + +/// `ConstantBackpropagationOptimizer` will attempt to determine any constant witnesses within the program. +/// It does this by attempting to solve the program without any inputs (i.e. using an empty witness map), +/// any values which it can determine are then enforced to be constant values. +/// +/// The optimizer will then replace any witnesses wherever they appear within the circuit with these constant values. +/// This is repeated until the circuit stabilizes. +pub(crate) struct ConstantBackpropagationOptimizer { + circuit: Circuit, +} + +impl ConstantBackpropagationOptimizer { + /// Creates a new `ConstantBackpropagationOptimizer` + pub(crate) fn new(circuit: Circuit) -> Self { + Self { circuit } + } + + fn gather_known_witnesses(&self) -> (WitnessMap, BTreeSet) { + // We do not want to affect the circuit's interface so avoid optimizing away these witnesses. + let mut required_witnesses: BTreeSet = self + .circuit + .private_parameters + .union(&self.circuit.public_parameters.0) + .chain(&self.circuit.return_values.0) + .copied() + .collect(); + + for opcode in &self.circuit.opcodes { + match &opcode { + Opcode::BlackBoxFuncCall(func_call) => { + required_witnesses.extend( + func_call.get_inputs_vec().into_iter().map(|func_input| func_input.witness), + ); + required_witnesses.extend(func_call.get_outputs_vec()); + } + + Opcode::MemoryInit { init, .. } => { + required_witnesses.extend(init); + } + + Opcode::MemoryOp { op, .. } => { + required_witnesses.insert(op.index.to_witness().unwrap()); + required_witnesses.insert(op.value.to_witness().unwrap()); + } + + _ => (), + }; + } + + let mut known_witnesses = WitnessMap::new(); + for opcode in self.circuit.opcodes.iter().rev() { + if let Opcode::AssertZero(expr) = opcode { + let solve_result = ExpressionSolver::solve(&mut known_witnesses, expr); + // It doesn't matter what the result is. We expect most opcodes to not be solved successfully so we discard errors. + // At the same time, if the expression can be solved then we track this by the updates to `known_witnesses` + drop(solve_result); + } + } + + // We want to retain any references to required witnesses so we "forget" these assignments. + let known_witnesses: BTreeMap<_, _> = known_witnesses + .into_iter() + .filter(|(witness, _)| !required_witnesses.contains(witness)) + .collect(); + + (known_witnesses.into(), required_witnesses) + } + + /// Returns a `Circuit` where with any constant witnesses replaced with the constant they resolve to. + #[tracing::instrument(level = "trace", skip_all)] + pub(crate) fn backpropagate_constants( + circuit: Circuit, + order_list: Vec, + ) -> (Circuit, Vec) { + let old_circuit_size = circuit.opcodes.len(); + + let optimizer = Self::new(circuit); + let (circuit, order_list) = optimizer.backpropagate_constants_iteration(order_list); + + let new_circuit_size = circuit.opcodes.len(); + if new_circuit_size < old_circuit_size { + Self::backpropagate_constants(circuit, order_list) + } else { + (circuit, order_list) + } + } + + /// Applies a single round of constant backpropagation to a `Circuit`. + pub(crate) fn backpropagate_constants_iteration( + mut self, + order_list: Vec, + ) -> (Circuit, Vec) { + let (mut known_witnesses, required_witnesses) = self.gather_known_witnesses(); + + let opcodes = std::mem::take(&mut self.circuit.opcodes); + + fn remap_expression(known_witnesses: &WitnessMap, expression: Expression) -> Expression { + GeneralOptimizer::optimize(ExpressionSolver::evaluate(&expression, known_witnesses)) + } + + let mut new_order_list = Vec::with_capacity(order_list.len()); + let mut new_opcodes = Vec::with_capacity(opcodes.len()); + for (idx, opcode) in opcodes.into_iter().enumerate() { + let new_opcode = match opcode { + Opcode::AssertZero(expression) => { + let new_expr = remap_expression(&known_witnesses, expression); + if new_expr.is_zero() { + continue; + } + + // Attempt to solve the opcode to see if we can determine the value of any witnesses in the expression. + // We only do this _after_ we apply any simplifications to create the new opcode as we want to + // keep the constraint on the witness which we are solving for here. + let solve_result = ExpressionSolver::solve(&mut known_witnesses, &new_expr); + // It doesn't matter what the result is. We expect most opcodes to not be solved successfully so we discard errors. + // At the same time, if the expression can be solved then we track this by the updates to `known_witnesses` + drop(solve_result); + + Opcode::AssertZero(new_expr) + } + Opcode::Brillig(brillig) => { + let remapped_inputs = brillig + .inputs + .into_iter() + .map(|input| match input { + BrilligInputs::Single(expr) => { + BrilligInputs::Single(remap_expression(&known_witnesses, expr)) + } + BrilligInputs::Array(expr_array) => { + let new_input: Vec<_> = expr_array + .into_iter() + .map(|expr| remap_expression(&known_witnesses, expr)) + .collect(); + + BrilligInputs::Array(new_input) + } + input @ BrilligInputs::MemoryArray(_) => input, + }) + .collect(); + + let remapped_predicate = brillig + .predicate + .map(|predicate| remap_expression(&known_witnesses, predicate)); + + let new_brillig = Brillig { + inputs: remapped_inputs, + predicate: remapped_predicate, + ..brillig + }; + + let brillig_output_is_required_witness = + new_brillig.outputs.iter().any(|output| match output { + BrilligOutputs::Simple(witness) => required_witnesses.contains(witness), + BrilligOutputs::Array(witness_array) => witness_array + .iter() + .any(|witness| required_witnesses.contains(witness)), + }); + + if brillig_output_is_required_witness { + // If one of the brillig opcode's outputs is a required witness then we can't remove the opcode. In this case we can't replace + // all of the uses of this witness with the calculated constant so we'll be attempting to use an uninitialized witness. + // + // We then do not attempt execution of this opcode and just simplify the inputs. + Opcode::Brillig(new_brillig) + } else if let Ok(mut solver) = BrilligSolver::new( + &known_witnesses, + &HashMap::new(), + &new_brillig, + &StubbedBlackBoxSolver, + idx, + ) { + match solver.solve() { + Ok(BrilligSolverStatus::Finished) => { + // Write execution outputs + match solver.finalize(&mut known_witnesses, &new_brillig) { + Ok(()) => { + // If we've managed to execute the brillig opcode at compile time, we can now just write in the + // results as constants for the rest of the circuit. + continue; + } + _ => Opcode::Brillig(new_brillig), + } + } + Ok(BrilligSolverStatus::InProgress) => unreachable!( + "Solver should either finish, block on foreign call, or error." + ), + Ok(BrilligSolverStatus::ForeignCallWait(_)) | Err(_) => { + Opcode::Brillig(new_brillig) + } + } + } else { + Opcode::Brillig(new_brillig) + } + } + + Opcode::Directive(Directive::ToLeRadix { a, b, radix }) => { + if b.iter().all(|output| known_witnesses.contains_key(output)) { + continue; + } else if b.iter().any(|witness| required_witnesses.contains(witness)) { + // If one of the brillig opcode's outputs is a required witness then we can't remove the opcode. In this case we can't replace + // all of the uses of this witness with the calculated constant so we'll be attempting to use an uninitialized witness. + // + // We then do not attempt execution of this opcode and just simplify the inputs. + Opcode::Directive(Directive::ToLeRadix { + a: remap_expression(&known_witnesses, a), + b, + radix, + }) + } else { + let directive = Directive::ToLeRadix { + a: remap_expression(&known_witnesses, a), + b, + radix, + }; + let result = solve_directives(&mut known_witnesses, &directive); + + match result { + Ok(()) => continue, + Err(_) => Opcode::Directive(directive), + } + } + } + + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + if solve_range_opcode(&known_witnesses, &input).is_ok() { + continue; + } else { + opcode + } + } + + Opcode::BlackBoxFuncCall(_) + | Opcode::MemoryOp { .. } + | Opcode::MemoryInit { .. } => opcode, + }; + + new_opcodes.push(new_opcode); + new_order_list.push(order_list[idx]); + } + + self.circuit.opcodes = new_opcodes; + + (self.circuit, new_order_list) + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeSet; + + use crate::compiler::optimizers::constant_backpropagation::ConstantBackpropagationOptimizer; + use acir::{ + brillig::MemoryAddress, + circuit::{ + brillig::{Brillig, BrilligOutputs}, + opcodes::{BlackBoxFuncCall, FunctionInput}, + Circuit, ExpressionWidth, Opcode, PublicInputs, + }, + native_types::Witness, + }; + use brillig_vm::brillig::Opcode as BrilligOpcode; + + fn test_circuit(opcodes: Vec) -> Circuit { + Circuit { + current_witness_index: 1, + expression_width: ExpressionWidth::Bounded { width: 3 }, + opcodes, + private_parameters: BTreeSet::new(), + public_parameters: PublicInputs::default(), + return_values: PublicInputs::default(), + assert_messages: Default::default(), + recursive: false, + } + } + + #[test] + fn retain_brillig_with_required_witness_outputs() { + let brillig_opcode = Opcode::Brillig(Brillig { + inputs: Vec::new(), + outputs: vec![BrilligOutputs::Simple(Witness(1))], + bytecode: vec![ + BrilligOpcode::Const { + destination: MemoryAddress(0), + bit_size: 32, + value: 1u128.into(), + }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, + ], + predicate: None, + }); + let blackbox_opcode = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AND { + lhs: FunctionInput { witness: Witness(1), num_bits: 64 }, + rhs: FunctionInput { witness: Witness(2), num_bits: 64 }, + output: Witness(3), + }); + + let opcodes = vec![brillig_opcode, blackbox_opcode]; + // The optimizer should keep the lowest bit size range constraint + let circuit = test_circuit(opcodes); + let acir_opcode_positions = circuit.opcodes.iter().enumerate().map(|(i, _)| i).collect(); + let optimizer = ConstantBackpropagationOptimizer::new(circuit); + + let (optimized_circuit, _) = + optimizer.backpropagate_constants_iteration(acir_opcode_positions); + + assert_eq!( + optimized_circuit.opcodes.len(), + 2, + "The brillig opcode should not be removed as the output is needed as a witness" + ); + } +} diff --git a/acvm-repo/acvm/src/compiler/optimizers/general.rs b/acvm-repo/acvm/src/compiler/optimizers/general.rs index 2bd781f7bb5..a48a590a05e 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/general.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/general.rs @@ -13,7 +13,8 @@ impl GeneralOptimizer { pub(crate) fn optimize(opcode: Expression) -> Expression { // XXX: Perhaps this optimization can be done on the fly let opcode = remove_zero_coefficients(opcode); - simplify_mul_terms(opcode) + let opcode = simplify_mul_terms(opcode); + simplify_linear_terms(opcode) } } @@ -42,3 +43,20 @@ fn simplify_mul_terms(mut gate: Expression) -> Expression { gate.mul_terms = hash_map.into_iter().map(|((w_l, w_r), scale)| (scale, w_l, w_r)).collect(); gate } + +// Simplifies all linear terms with the same variables +fn simplify_linear_terms(mut gate: Expression) -> Expression { + let mut hash_map: IndexMap = IndexMap::new(); + + // Canonicalize the ordering of the terms, lets just order by variable name + for (scale, witness) in gate.linear_combinations.into_iter() { + *hash_map.entry(witness).or_insert_with(FieldElement::zero) += scale; + } + + gate.linear_combinations = hash_map + .into_iter() + .filter(|(_, scale)| scale != &FieldElement::zero()) + .map(|(witness, scale)| (scale, witness)) + .collect(); + gate +} diff --git a/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 923756580b3..599bdabd420 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -1,5 +1,6 @@ use acir::circuit::{Circuit, Opcode}; +mod constant_backpropagation; mod general; mod redundant_range; mod unused_memory; @@ -8,6 +9,7 @@ pub(crate) use general::GeneralOptimizer; pub(crate) use redundant_range::RangeOptimizer; use tracing::info; +use self::constant_backpropagation::ConstantBackpropagationOptimizer; use self::unused_memory::UnusedMemoryOptimizer; use super::{transform_assert_messages, AcirTransformationMap}; @@ -26,6 +28,15 @@ pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) { /// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. #[tracing::instrument(level = "trace", name = "optimize_acir" skip(acir))] pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec) { + // Track original acir opcode positions throughout the transformation passes of the compilation + // by applying the modifications done to the circuit opcodes and also to the opcode_positions (delete and insert) + let acir_opcode_positions = (0..acir.opcodes.len()).collect(); + + if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::Brillig(_)) { + info!("Program is fully unconstrained, skipping optimization pass"); + return (acir, acir_opcode_positions); + } + info!("Number of opcodes before: {}", acir.opcodes.len()); // General optimizer pass @@ -42,20 +53,22 @@ pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec) { .collect(); let acir = Circuit { opcodes, ..acir }; - // Track original acir opcode positions throughout the transformation passes of the compilation - // by applying the modifications done to the circuit opcodes and also to the opcode_positions (delete and insert) - let acir_opcode_positions = (0..acir.opcodes.len()).collect(); - // Unused memory optimization pass let memory_optimizer = UnusedMemoryOptimizer::new(acir); let (acir, acir_opcode_positions) = memory_optimizer.remove_unused_memory_initializations(acir_opcode_positions); + let (acir, acir_opcode_positions) = + ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + // Range optimization pass let range_optimizer = RangeOptimizer::new(acir); let (acir, acir_opcode_positions) = range_optimizer.replace_redundant_ranges(acir_opcode_positions); + let (acir, acir_opcode_positions) = + ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + info!("Number of opcodes after: {}", acir.opcodes.len()); (acir, acir_opcode_positions) diff --git a/acvm-repo/acvm/src/pwg/arithmetic.rs b/acvm-repo/acvm/src/pwg/arithmetic.rs index 81462ea495e..dc9e13d44b6 100644 --- a/acvm-repo/acvm/src/pwg/arithmetic.rs +++ b/acvm-repo/acvm/src/pwg/arithmetic.rs @@ -7,7 +7,7 @@ use super::{insert_value, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionErro /// An Expression solver will take a Circuit's assert-zero opcodes with witness assignments /// and create the other witness variables -pub(super) struct ExpressionSolver; +pub(crate) struct ExpressionSolver; #[allow(clippy::enum_variant_names)] pub(super) enum OpcodeStatus { @@ -24,13 +24,18 @@ pub(crate) enum MulTerm { impl ExpressionSolver { /// Derives the rest of the witness based on the initial low level variables - pub(super) fn solve( + pub(crate) fn solve( initial_witness: &mut WitnessMap, opcode: &Expression, ) -> Result<(), OpcodeResolutionError> { let opcode = &ExpressionSolver::evaluate(opcode, initial_witness); // Evaluate multiplication term - let mul_result = ExpressionSolver::solve_mul_term(opcode, initial_witness); + let mul_result = + ExpressionSolver::solve_mul_term(opcode, initial_witness).map_err(|_| { + OpcodeResolutionError::OpcodeNotSolvable( + OpcodeNotSolvable::ExpressionHasTooManyUnknowns(opcode.clone()), + ) + })?; // Evaluate the fan-in terms let opcode_status = ExpressionSolver::solve_fan_in_term(opcode, initial_witness); @@ -54,9 +59,7 @@ impl ExpressionSolver { } } else { let assignment = -total_sum / (q + b); - // Add this into the witness assignments - insert_value(&w1, assignment, initial_witness)?; - Ok(()) + insert_value(&w1, assignment, initial_witness) } } else { // TODO: can we be more specific with this error? @@ -84,9 +87,7 @@ impl ExpressionSolver { } } else { let assignment = -(total_sum / partial_prod); - // Add this into the witness assignments - insert_value(&unknown_var, assignment, initial_witness)?; - Ok(()) + insert_value(&unknown_var, assignment, initial_witness) } } (MulTerm::Solved(a), OpcodeStatus::OpcodeSatisfied(b)) => { @@ -118,9 +119,7 @@ impl ExpressionSolver { } } else { let assignment = -(total_sum / coeff); - // Add this into the witness assignments - insert_value(&unknown_var, assignment, initial_witness)?; - Ok(()) + insert_value(&unknown_var, assignment, initial_witness) } } } @@ -130,16 +129,19 @@ impl ExpressionSolver { /// If the witness values are not known, then the function returns a None /// XXX: Do we need to account for the case where 5xy + 6x = 0 ? We do not know y, but it can be solved given x . But I believe x can be solved with another opcode /// XXX: What about making a mul opcode = a constant 5xy + 7 = 0 ? This is the same as the above. - fn solve_mul_term(arith_opcode: &Expression, witness_assignments: &WitnessMap) -> MulTerm { + fn solve_mul_term( + arith_opcode: &Expression, + witness_assignments: &WitnessMap, + ) -> Result { // First note that the mul term can only contain one/zero term // We are assuming it has been optimized. match arith_opcode.mul_terms.len() { - 0 => MulTerm::Solved(FieldElement::zero()), - 1 => ExpressionSolver::solve_mul_term_helper( + 0 => Ok(MulTerm::Solved(FieldElement::zero())), + 1 => Ok(ExpressionSolver::solve_mul_term_helper( &arith_opcode.mul_terms[0], witness_assignments, - ), - _ => panic!("Mul term in the assert-zero opcode must contain either zero or one term"), + )), + _ => Err(OpcodeStatus::OpcodeUnsolvable), } } @@ -209,7 +211,7 @@ impl ExpressionSolver { } // Partially evaluate the opcode using the known witnesses - pub(super) fn evaluate(expr: &Expression, initial_witness: &WitnessMap) -> Expression { + pub(crate) fn evaluate(expr: &Expression, initial_witness: &WitnessMap) -> Expression { let mut result = Expression::default(); for &(c, w1, w2) in &expr.mul_terms { let mul_result = ExpressionSolver::solve_mul_term_helper(&(c, w1, w2), initial_witness); diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 4309cad1b2e..6ee926043cd 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -25,7 +25,7 @@ use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul}; use hash::{solve_generic_256_hash_opcode, solve_sha_256_permutation_opcode}; use logic::{and, xor}; use pedersen::pedersen; -use range::solve_range_opcode; +pub(crate) use range::solve_range_opcode; use signature::{ ecdsa::{secp256k1_prehashed, secp256r1_prehashed}, schnorr::schnorr_verify, diff --git a/acvm-repo/acvm/src/pwg/blackbox/range.rs b/acvm-repo/acvm/src/pwg/blackbox/range.rs index 1b976e30ed5..2afe820b636 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -4,7 +4,7 @@ use crate::{ }; use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap}; -pub(super) fn solve_range_opcode( +pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, input: &FunctionInput, ) -> Result<(), OpcodeResolutionError> { diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index b0fb7469fd9..51c7f4c6203 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -65,7 +65,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { /// Constructs a solver for a Brillig block given the bytecode and initial /// witness. - pub(super) fn new( + pub(crate) fn new( initial_witness: &WitnessMap, memory: &HashMap, brillig: &'b Brillig, @@ -134,7 +134,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { self.vm.get_call_stack() } - pub(super) fn solve(&mut self) -> Result { + pub(crate) fn solve(&mut self) -> Result { let status = self.vm.process_opcodes(); self.handle_vm_status(status) } @@ -177,7 +177,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { } } - pub(super) fn finalize( + pub(crate) fn finalize( self, witness: &mut WitnessMap, brillig: &Brillig, diff --git a/acvm-repo/acvm/src/pwg/directives/mod.rs b/acvm-repo/acvm/src/pwg/directives/mod.rs index 07226c85b27..ee544521fc7 100644 --- a/acvm-repo/acvm/src/pwg/directives/mod.rs +++ b/acvm-repo/acvm/src/pwg/directives/mod.rs @@ -11,7 +11,7 @@ use super::{get_value, insert_value, ErrorLocation}; /// Returns `Ok(OpcodeResolution)` to signal whether the directive was successful solved. /// /// Returns `Err(OpcodeResolutionError)` if a circuit constraint is unsatisfied. -pub(super) fn solve_directives( +pub(crate) fn solve_directives( initial_witness: &mut WitnessMap, directive: &Directive, ) -> Result<(), OpcodeResolutionError> { diff --git a/acvm-repo/acvm/src/pwg/memory_op.rs b/acvm-repo/acvm/src/pwg/memory_op.rs index 49ec652289e..e51797707a7 100644 --- a/acvm-repo/acvm/src/pwg/memory_op.rs +++ b/acvm-repo/acvm/src/pwg/memory_op.rs @@ -13,7 +13,7 @@ type MemoryIndex = u32; /// Maintains the state for solving [`MemoryInit`][`acir::circuit::Opcode::MemoryInit`] and [`MemoryOp`][`acir::circuit::Opcode::MemoryOp`] opcodes. #[derive(Default)] -pub(super) struct MemoryOpSolver { +pub(crate) struct MemoryOpSolver { pub(super) block_value: HashMap, pub(super) block_len: u32, } diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 2ee39a289e7..d8323e5ef5f 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -21,11 +21,11 @@ use thiserror::Error; // arithmetic pub(crate) mod arithmetic; // Brillig bytecode -mod brillig; +pub(crate) mod brillig; // Directives -mod directives; +pub(crate) mod directives; // black box functions -mod blackbox; +pub(crate) mod blackbox; mod memory_op; pub use self::brillig::{BrilligSolver, BrilligSolverStatus}; diff --git a/cspell.json b/cspell.json index 23659b39c68..a96e3de901a 100644 --- a/cspell.json +++ b/cspell.json @@ -13,6 +13,8 @@ "arithmetization", "arity", "arkworks", + "backpropagate", + "Backpropagation", "barebones", "barretenberg", "bincode",