@@ -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" ) ;
0 commit comments