Skip to content

Commit fad7755

Browse files
authored
Reduce visibility. (#3295)
This PR changes `pub(crate)` visibility of the fields of `GroupedExpression` back to `non-pub` as it was before the extraction of the solving routines. The solving routines do not really need write access to those fields and read-access is obtained using `components()`.
1 parent e47f716 commit fad7755

File tree

4 files changed

+61
-45
lines changed

4 files changed

+61
-45
lines changed

constraint-solver/src/algebraic_constraint/solve.rs

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -83,26 +83,27 @@ where
8383
/// `variable` does not appear in the quadratic component and
8484
/// has a coefficient which is known to be not zero.
8585
///
86+
/// If the constraint has the form `A + k * x = 0` where `A` does not
87+
/// contain the variable `x` and `k` is a non-zero runtime constant,
88+
/// it returns `A * (-k^(-1))`.
89+
///
8690
/// Returns the resulting solved grouped expression.
8791
pub fn try_solve_for(&self, variable: &V) -> Option<GroupedExpression<T, V>> {
88-
let expression = self.expression;
89-
90-
if expression
91-
.quadratic
92-
.iter()
93-
.flat_map(|(l, r)| [l, r])
94-
.flat_map(|c| c.referenced_unknown_variables())
95-
.contains(variable)
96-
{
97-
// The variable is in the quadratic component, we cannot solve for it.
92+
let coefficient = self
93+
.expression
94+
.coefficient_of_variable_in_affine_part(variable)?;
95+
if !coefficient.is_known_nonzero() {
9896
return None;
9997
}
100-
if !expression.linear.get(variable)?.is_known_nonzero() {
98+
99+
let subtracted = self.expression.clone()
100+
- GroupedExpression::from_unknown_variable(variable.clone()) * coefficient.clone();
101+
if subtracted.referenced_unknown_variables().contains(variable) {
102+
// There is another occurrence of the variable in the quadratic component,
103+
// we cannot solve for it.
101104
return None;
102105
}
103-
let mut result = self.expression.clone();
104-
let coefficient = result.linear.remove(variable)?;
105-
Some(result * (-T::one().field_div(&coefficient)))
106+
Some(subtracted * (-T::one().field_div(coefficient)))
106107
}
107108

108109
/// Algebraically transforms the constraint such that `self = 0` is equivalent
@@ -128,10 +129,14 @@ where
128129
let normalization_factor = expr
129130
.referenced_unknown_variables()
130131
.find_map(|var| {
131-
let coeff = expression.coefficient_of_variable(var)?;
132+
let coeff = expression.coefficient_of_variable_in_affine_part(var)?;
132133
// We can only divide if we know the coefficient is non-zero.
133134
if coeff.is_known_nonzero() {
134-
Some(expr.coefficient_of_variable(var).unwrap().field_div(coeff))
135+
Some(
136+
expr.coefficient_of_variable_in_affine_part(var)
137+
.unwrap()
138+
.field_div(coeff),
139+
)
135140
} else {
136141
None
137142
}
@@ -159,27 +164,27 @@ where
159164
&self,
160165
range_constraints: &impl RangeConstraintProvider<T::FieldType, V>,
161166
) -> Result<ProcessResult<T, V>, Error> {
162-
let expression = self.expression;
167+
let (_, mut linear, constant) = self.expression.components();
163168

164-
Ok(if expression.linear.len() == 1 {
165-
let (var, coeff) = expression.linear.iter().next().unwrap();
169+
Ok(if linear.clone().count() == 1 {
170+
let (var, coeff) = linear.next().unwrap();
166171
// Solve "coeff * X + self.constant = 0" by division.
167172
assert!(
168173
!coeff.is_known_zero(),
169174
"Zero coefficient has not been removed: {self}"
170175
);
171176
if coeff.is_known_nonzero() {
172177
// In this case, we can always compute a solution.
173-
let value = expression.constant.field_div(&-coeff.clone());
178+
let value = constant.field_div(&-coeff.clone());
174179
ProcessResult::complete(vec![assignment_if_satisfies_range_constraints(
175180
var.clone(),
176181
value,
177182
range_constraints,
178183
)?])
179-
} else if expression.constant.is_known_nonzero() {
184+
} else if constant.is_known_nonzero() {
180185
// If the offset is not zero, then the coefficient must be non-zero,
181186
// otherwise the constraint is violated.
182-
let value = expression.constant.field_div(&-coeff.clone());
187+
let value = constant.field_div(&-coeff.clone());
183188
ProcessResult::complete(vec![
184189
Assertion::assert_is_nonzero(coeff.clone()),
185190
assignment_if_satisfies_range_constraints(
@@ -208,13 +213,12 @@ where
208213
&self,
209214
range_constraints: &impl RangeConstraintProvider<T::FieldType, V>,
210215
) -> Vec<Effect<T, V>> {
211-
let expression = self.expression;
212216
// Solve for each of the variables in the linear component and
213217
// compute the range constraints.
214-
assert!(!expression.is_quadratic());
215-
expression
216-
.linear
217-
.iter()
218+
assert!(!self.expression.is_quadratic());
219+
self.expression
220+
.components()
221+
.1
218222
.filter_map(|(var, _)| {
219223
let rc = self.try_solve_for(var)?.range_constraint(range_constraints);
220224
Some((var, rc))
@@ -852,7 +856,7 @@ mod tests {
852856

853857
expr.substitute_by_unknown(&"x", &subst);
854858
assert!(!expr.is_quadratic());
855-
assert_eq!(expr.linear.len(), 3);
859+
assert_eq!(expr.components().1.count(), 3);
856860
assert_eq!(expr.to_string(), "a + b + y");
857861
}
858862

@@ -874,14 +878,17 @@ mod tests {
874878
);
875879
// Structural validation
876880
assert_eq!(quadratic.len(), 2);
877-
assert_eq!(quadratic[0].0.to_string(), "(a) * (b) + 1");
878-
assert_eq!(quadratic[0].0.quadratic[0].0.to_string(), "a");
879-
assert_eq!(quadratic[0].0.quadratic[0].1.to_string(), "b");
880-
assert!(quadratic[0].0.linear.is_empty());
881-
assert_eq!(
882-
quadratic[0].0.constant.try_to_number(),
883-
Some(GoldilocksField::from(1)),
884-
);
881+
{
882+
assert_eq!(quadratic[0].0.to_string(), "(a) * (b) + 1");
883+
let (inner_quadratic, inner_linear, inner_constant) = quadratic[0].0.components();
884+
assert_eq!(inner_quadratic[0].0.to_string(), "a");
885+
assert_eq!(inner_quadratic[0].1.to_string(), "b");
886+
assert!(inner_linear.count() == 0);
887+
assert_eq!(
888+
inner_constant.try_to_number(),
889+
Some(GoldilocksField::from(1)),
890+
);
891+
}
885892
assert_eq!(quadratic[0].1.to_string(), "w");
886893
assert_eq!(quadratic[1].0.to_string(), "a");
887894
assert_eq!(quadratic[1].1.to_string(), "b");

constraint-solver/src/constraint_system.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,10 @@ impl<
189189
// `k * var + e` is in range rc <=>
190190
// `var` is in range `(rc - RC[e]) / k` = `rc / k + RC[-e / k]`
191191
// If we solve `expr` for `var`, we get `-e / k`.
192-
let k = expr.coefficient_of_variable(var).unwrap().try_to_number()?;
192+
let k = expr
193+
.coefficient_of_variable_in_affine_part(var)
194+
.unwrap()
195+
.try_to_number()?;
193196
let expr = AlgebraicConstraint::assert_zero(expr).try_solve_for(var)?;
194197
let rc = rc
195198
.multiple(T::FieldType::from(1) / k)

constraint-solver/src/grouped_expression.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ pub struct GroupedExpression<T, V> {
3939
/// Quadratic terms of the form `a * X * Y`, where `a` is a (symbolically)
4040
/// known value and `X` and `Y` are grouped expressions that
4141
/// have at least one unknown.
42-
pub(crate) quadratic: Vec<(Self, Self)>,
42+
quadratic: Vec<(Self, Self)>,
4343
/// Linear terms of the form `a * X`, where `a` is a (symbolically) known
4444
/// value and `X` is an unknown variable.
45-
pub(crate) linear: BTreeMap<V, T>,
45+
linear: BTreeMap<V, T>,
4646
/// Constant term, a (symbolically) known value.
47-
pub(crate) constant: T,
47+
constant: T,
4848
}
4949

5050
impl<F: FieldElement, T: RuntimeConstant<FieldType = F>, V> GroupedExpression<T, V> {
@@ -236,10 +236,13 @@ impl<T: RuntimeConstant, V: Ord + Clone + Eq> GroupedExpression<T, V> {
236236
.unwrap()
237237
}
238238

239-
/// Returns the coefficient of the variable `variable` if this is an affine expression.
240-
/// Panics if the expression is quadratic.
241-
pub fn coefficient_of_variable<'a>(&'a self, var: &V) -> Option<&'a T> {
242-
assert!(!self.is_quadratic());
239+
/// Returns the coefficient of the variable `variable` in the affine part of this
240+
/// expression.
241+
/// If the expression is affine, this is the actual coefficient of the variable
242+
/// in the expression. Otherwise, the quadratic part of the expression could
243+
/// also contain the variable and thus the actual coefficient might be different
244+
/// (even zero).
245+
pub fn coefficient_of_variable_in_affine_part<'a>(&'a self, var: &V) -> Option<&'a T> {
243246
self.linear.get(var)
244247
}
245248

constraint-solver/src/solver/quadratic_equivalences.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ impl<T: RuntimeConstant, V: Ord + Clone + Hash + Eq> QuadraticEqualityCandidate<
128128
/// Returns an equivalent candidate that is normalized
129129
/// such that `var` has a coefficient of `1`.
130130
fn normalized_for_var(&self, var: &V) -> Self {
131-
let coefficient = self.expr.coefficient_of_variable(var).unwrap();
131+
let coefficient = self
132+
.expr
133+
.coefficient_of_variable_in_affine_part(var)
134+
.unwrap();
132135

133136
// self represents
134137
// `(coeff * var + X) * (coeff * var + X + offset) = 0`

0 commit comments

Comments
 (0)