Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

chore!: Reorganise compiler in terms of optimisers and transformers #88

Merged
merged 2 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion acir/src/circuit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const VERSION_NUMBER: u32 = 0;

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct Circuit {
// current_witness_index is the highest witness index in the circuit. The next witness to be added to this circuit
// will take on this value. (The value is cached here as an optimization.)
pub current_witness_index: u32,
pub opcodes: Vec<Opcode>,
pub public_inputs: PublicInputs,
Expand Down Expand Up @@ -204,7 +206,7 @@ mod test {
Opcode::Arithmetic(crate::native_types::Expression {
mul_terms: vec![],
linear_combinations: vec![],
q_c: FieldElement::from_hex("FFFF").unwrap(),
q_c: FieldElement::from(8u128),
}),
range_opcode(),
and_opcode(),
Expand Down
1 change: 1 addition & 0 deletions acir/src/native_types/witness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use flate2::{
};
use serde::{Deserialize, Serialize};

// Witness might be a misnomer. This is an index that represents the position a witness will take
#[derive(
Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize,
)]
Expand Down
56 changes: 35 additions & 21 deletions acvm/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// The various passes that we can use over ACIR
pub mod fallback;
pub mod optimizer;
pub mod optimizers;
pub mod transformers;

use crate::Language;
use acir::{
Expand All @@ -9,10 +9,9 @@ use acir::{
BlackBoxFunc,
};
use indexmap::IndexMap;
use optimizer::{CSatOptimizer, GeneralOptimizer};
use optimizers::GeneralOptimizer;
use thiserror::Error;

use self::{fallback::IsBlackBoxSupported, optimizer::R1CSOptimizer};
use transformers::{CSatTransformer, FallbackTransformer, IsBlackBoxSupported, R1CSTransformer};

#[derive(PartialEq, Eq, Debug, Error)]
pub enum CompileError {
Expand All @@ -29,33 +28,48 @@ pub fn compile(
// Currently the optimizer and reducer are one in the same
// for CSAT

// Fallback pass
let fallback = fallback::fallback(acir, is_black_box_supported)?;
// Fallback transformer pass
let acir = FallbackTransformer::transform(acir, is_black_box_supported)?;

// General optimizer pass
let mut opcodes: Vec<Opcode> = Vec::new();
for opcode in acir.opcodes {
match opcode {
Opcode::Arithmetic(arith_expr) => {
opcodes.push(Opcode::Arithmetic(GeneralOptimizer::optimize(arith_expr)))
}
other_gate => opcodes.push(other_gate),
};
}
let acir = Circuit { opcodes, ..acir };

let optimizer = match &np_language {
let transformer = match &np_language {
crate::Language::R1CS => {
let optimizer = R1CSOptimizer::new(fallback);
return Ok(optimizer.optimize());
let transformer = R1CSTransformer::new(acir);
return Ok(transformer.transform());
}
crate::Language::PLONKCSat { width } => CSatOptimizer::new(*width),
crate::Language::PLONKCSat { width } => CSatTransformer::new(*width),
};

// TODO: the code below is only for CSAT optimizer
// TODO: the code below is only for CSAT transformer
// TODO it may be possible to refactor it in a way that we do not need to return early from the r1cs
// TODO or at the very least, we could put all of it inside of CSatOptimizer pass

// Optimize the arithmetic gates by reducing them into the correct width and
// creating intermediate variables when necessary
let mut optimized_gates = Vec::new();
let mut transformed_gates = Vec::new();

let mut next_witness_index = fallback.current_witness_index + 1;
for opcode in fallback.opcodes {
let mut next_witness_index = acir.current_witness_index + 1;
for opcode in acir.opcodes {
match opcode {
Opcode::Arithmetic(arith_expr) => {
let mut intermediate_variables: IndexMap<Witness, Expression> = IndexMap::new();

let arith_expr =
optimizer.optimize(arith_expr, &mut intermediate_variables, next_witness_index);
let arith_expr = transformer.transform(
arith_expr,
&mut intermediate_variables,
next_witness_index,
);

// Update next_witness counter
next_witness_index += intermediate_variables.len() as u32;
Expand All @@ -67,18 +81,18 @@ pub fn compile(
new_gates.push(arith_expr);
new_gates.sort();
for gate in new_gates {
optimized_gates.push(Opcode::Arithmetic(gate));
transformed_gates.push(Opcode::Arithmetic(gate));
}
}
other_gate => optimized_gates.push(other_gate),
other_gate => transformed_gates.push(other_gate),
}
}

let current_witness_index = next_witness_index - 1;

Ok(Circuit {
current_witness_index,
opcodes: optimized_gates,
public_inputs: fallback.public_inputs, // The optimizer does not add public inputs
opcodes: transformed_gates,
public_inputs: acir.public_inputs, // The optimizer does not add public inputs
})
}
97 changes: 0 additions & 97 deletions acvm/src/compiler/fallback.rs

This file was deleted.

7 changes: 0 additions & 7 deletions acvm/src/compiler/optimizer/mod.rs

This file was deleted.

35 changes: 0 additions & 35 deletions acvm/src/compiler/optimizer/r1cs_optimizer.rs

This file was deleted.

3 changes: 3 additions & 0 deletions acvm/src/compiler/optimizers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod general;

pub use general::GeneralOpt as GeneralOptimizer;
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,30 @@ use acir::{
};
use indexmap::IndexMap;

use super::general_optimizer::GeneralOpt;
// Optimizer struct with all of the related optimizations to the arithmetic gate

// Is this more of a Reducer than an optimizer?
// Should we give it all of the gates?
// Have a single optimizer that you instantiate with a width, then pass many gates through
pub struct Optimizer {
// Have a single transformer that you instantiate with a width, then pass many gates through
pub struct CSatTransformer {
width: usize,
}

impl Optimizer {
// Configure the width for the Optimizer
pub fn new(width: usize) -> Optimizer {
impl CSatTransformer {
// Configure the width for the optimizer
pub fn new(width: usize) -> CSatTransformer {
assert!(width > 2);

Optimizer { width }
CSatTransformer { width }
}

// Still missing dead witness optimization.
// To do this, we will need the whole set of arithmetic gates
// I think it can also be done before the local optimization seen here, as dead variables will come from the user
pub fn optimize(
pub fn transform(
&self,
gate: Expression,
intermediate_variables: &mut IndexMap<Witness, Expression>,
num_witness: u32,
) -> Expression {
let gate = GeneralOpt::optimize(gate);

// Here we create intermediate variables and constrain them to be equal to any subset of the polynomial that can be represented as a full gate
let gate = self.full_gate_scan_optimization(gate, intermediate_variables, num_witness);
// The last optimization to do is to create intermediate variables in order to flatten the fan-in and the amount of mul terms
Expand Down Expand Up @@ -348,8 +343,9 @@ fn simple_reduction_smoke_test() {

let num_witness = 4;

let optimizer = Optimizer::new(3);
let got_optimized_gate_a = optimizer.optimize(gate_a, &mut intermediate_variables, num_witness);
let optimizer = CSatTransformer::new(3);
let got_optimized_gate_a =
optimizer.transform(gate_a, &mut intermediate_variables, num_witness);

// a = b + c + d => a - b - c - d = 0
// For width3, the result becomes:
Expand Down
Loading