diff --git a/CHANGELOG.md b/CHANGELOG.md index bce0b6613..2a318611a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Improvements - #325 Reduce memory consumption during inlining +- #339 Reduce memory consumption under `Constraints` optimization goal ### Bug fixes diff --git a/relations/src/r1cs/constraint_system.rs b/relations/src/r1cs/constraint_system.rs index 93b37bfd3..03c5d1073 100644 --- a/relations/src/r1cs/constraint_system.rs +++ b/relations/src/r1cs/constraint_system.rs @@ -249,6 +249,58 @@ impl ConstraintSystem { Ok(var) } + /// Optimized routine for multiplying a variable by a constant `other`. + #[inline] + pub fn mul_var_by_constant( + &mut self, + var: &Variable, + other: &F, + ) -> crate::r1cs::Result { + // Create a new symbolic LC using this constant, deferring the inlining + // until the finalization phase. + #[inline] + fn base_mul_by_constant( + cs: &mut ConstraintSystem, + var: &Variable, + other: F, + ) -> crate::r1cs::Result { + let variable = cs.new_lc(lc!() + (other, *var)).unwrap(); + Ok(variable) + } + + return match var { + Variable::Zero => Ok(Variable::Zero), + Variable::One => base_mul_by_constant(self, var, *other), + Variable::Instance(_) => base_mul_by_constant(self, var, *other), + Variable::Witness(_) => base_mul_by_constant(self, var, *other), + Variable::SymbolicLc(index) => { + if self.optimization_goal == OptimizationGoal::Constraints + || self.optimization_goal == OptimizationGoal::None + { + // In this case we are optimizing for the number of constraints, + // so we have no need for symbolic variables of intermediate computation steps. + // Thus we inline this multiplication by a constant immediately. + let lc = self.lc_map.get(&index).unwrap(); + let mut new_lc = lc.clone(); + for i in 0..new_lc.len() { + new_lc[i].0 *= other; + } + let variable = self.new_lc(new_lc).unwrap(); + return Ok(variable); + } else { + // In this case we are optimizing for constraint density, + // so we want to know when a symbolic LC is being re-used. + base_mul_by_constant(self, var, *other) + // TODO: If `var` is a symbolic LC with a single element, + // we don't need to make the new symbolic LC nest on top of var. + // It can instead re-use the single variable underlying `var`. + // This costs us an extra lookup though, so we should only do such + // a switch if benchmarks prove it to be useful. + } + } + }; + } + /// Enforce a R1CS constraint with the name `name`. #[inline] pub fn enforce_constraint(