Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: cleanup logic in mul_with_witness #1980

Merged
merged 10 commits into from
Jul 24, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,51 @@ impl GeneratedAcir {
pub(crate) fn push_return_witness(&mut self, witness: Witness) {
self.return_witnesses.push(witness);
}

/// Returns an expression which represents `lhs * rhs`
///
/// If one has multiplicative term and the other is of degree one or more,
/// the function creates [intermediate variables][`Witness`] accordingly.
/// There are two cases where we can optimize the multiplication between two expressions:
/// 1. If both expressions have at most a total degree of 1 in each term, then we can just multiply them
/// as each term in the result will be degree-2.
/// 2. If one expression is a constant, then we can just multiply the constant with the other expression
///
/// (1) is because an [`Expression`] can hold at most a degree-2 univariate polynomial
/// which is what you get when you multiply two degree-1 univariate polynomials.
pub(crate) fn mul_with_witness(&mut self, lhs: &Expression, rhs: &Expression) -> Expression {
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
use std::borrow::Cow;
let lhs_is_linear = lhs.is_linear();
let rhs_is_linear = rhs.is_linear();

// Case 1: Both expressions have at most a total degree of 1 in each term
if lhs_is_linear && rhs_is_linear {
return (lhs * rhs)
.expect("one of the expressions is a constant and so this should not fail");
}

// Case 2: One or both of the sides needs to be reduced to a degree-1 univariate polynomial
let lhs_reduced = if lhs_is_linear {
Cow::Borrowed(lhs)
} else {
Cow::Owned(self.get_or_create_witness(lhs).into())
};

// If the lhs and rhs are the same, then we do not need to reduce
// rhs, we only need to square the lhs.
if lhs == rhs {
return (&*lhs_reduced * &*lhs_reduced)
.expect("Both expressions are reduced to be degree<=1");
};

let rhs_reduced = if rhs_is_linear {
Cow::Borrowed(rhs)
} else {
Cow::Owned(self.get_or_create_witness(rhs).into())
};

(&*lhs_reduced * &*rhs_reduced).expect("Both expressions are reduced to be degree<=1")
}
}

impl GeneratedAcir {
Expand Down
30 changes: 1 addition & 29 deletions crates/noirc_evaluator/src/ssa_refactor/acir_gen/acir_ir/sort.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use acvm::acir::native_types::{Expression, Witness};

use super::generated_acir::GeneratedAcir;
use acvm::acir::native_types::{Expression, Witness};

impl GeneratedAcir {
// Generates gates for a sorting network
Expand Down Expand Up @@ -71,31 +70,4 @@ impl GeneratedAcir {
conf.extend(w2);
(conf, out_expr)
}

/// Returns an expression which represents a*b
/// If one has multiplicative term and the other is of degree one or more,
/// the function creates intermediate variables accordindly
pub(super) fn mul_with_witness(&mut self, a: &Expression, b: &Expression) -> Expression {
let a_arith;
let a_arith = if !a.mul_terms.is_empty() && !b.is_const() {
let a_witness = self.get_or_create_witness(a);
a_arith = Expression::from(a_witness);
&a_arith
} else {
a
};
let b_arith;
let b_arith = if !b.mul_terms.is_empty() && !a.is_const() {
if a == b {
a_arith
} else {
let b_witness = self.get_or_create_witness(a);
b_arith = Expression::from(b_witness);
&b_arith
}
} else {
b
};
(a_arith * b_arith).expect("Both expressions are reduced to be degree<=1")
}
}