@@ -48,25 +48,51 @@ impl RangeOptimizer {
4848 /// only store the fact that we have constrained it to
4949 /// be 16 bits.
5050 fn collect_ranges ( circuit : & Circuit ) -> BTreeMap < Witness , u32 > {
51- let mut witness_to_bit_sizes = BTreeMap :: new ( ) ;
51+ let mut witness_to_bit_sizes: BTreeMap < Witness , u32 > = BTreeMap :: new ( ) ;
5252
5353 for opcode in & circuit. opcodes {
54- // Extract the witness index and number of bits,
55- // if it is a range constraint
56- let ( witness, num_bits) = match extract_range_opcode ( opcode) {
57- Some ( func_inputs) => func_inputs,
58- None => continue ,
54+ let Some ( ( witness, num_bits) ) = ( match opcode {
55+ Opcode :: AssertZero ( expr) => {
56+ // If the opcode is constraining a witness to be equal to a value then it can be considered
57+ // as a range opcode for the number of bits required to hold that value.
58+ if expr. is_degree_one_univariate ( ) {
59+ let ( k, witness) = expr. linear_combinations [ 0 ] ;
60+ let constant = expr. q_c ;
61+ let witness_value = -constant / k;
62+
63+ if witness_value. is_zero ( ) {
64+ Some ( ( witness, 0 ) )
65+ } else {
66+ // We subtract off 1 bit from the implied witness value to give the weakest range constraint
67+ // which would be stricter than the constraint imposed by this opcode.
68+ let implied_range_constraint_bits = witness_value. num_bits ( ) - 1 ;
69+ Some ( ( witness, implied_range_constraint_bits) )
70+ }
71+ } else {
72+ None
73+ }
74+ }
75+
76+
77+ Opcode :: BlackBoxFuncCall ( BlackBoxFuncCall :: RANGE {
78+ input : FunctionInput { witness, num_bits } ,
79+ } ) => {
80+ Some ( ( * witness, * num_bits) )
81+ }
82+
83+ _ => None ,
84+ } ) else {
85+ continue ;
5986 } ;
6087
6188 // Check if the witness has already been recorded and if the witness
6289 // size is more than the current one, we replace it
63- let should_replace = match witness_to_bit_sizes. get ( & witness) . copied ( ) {
64- Some ( old_range_bits) => old_range_bits > num_bits,
65- None => true ,
66- } ;
67- if should_replace {
68- witness_to_bit_sizes. insert ( witness, num_bits) ;
69- }
90+ witness_to_bit_sizes
91+ . entry ( witness)
92+ . and_modify ( |old_range_bits| {
93+ * old_range_bits = std:: cmp:: min ( * old_range_bits, num_bits) ;
94+ } )
95+ . or_insert ( num_bits) ;
7096 }
7197 witness_to_bit_sizes
7298 }
@@ -116,16 +142,10 @@ impl RangeOptimizer {
116142/// Extract the range opcode from the `Opcode` enum
117143/// Returns None, if `Opcode` is not the range opcode.
118144fn extract_range_opcode ( opcode : & Opcode ) -> Option < ( Witness , u32 ) > {
119- // Range constraints are blackbox function calls
120- // so we first extract the function call
121- let func_call = match opcode {
122- acir:: circuit:: Opcode :: BlackBoxFuncCall ( func_call) => func_call,
123- _ => return None ,
124- } ;
125-
126- // Skip if it is not a range constraint
127- match func_call {
128- BlackBoxFuncCall :: RANGE { input } => Some ( ( input. witness , input. num_bits ) ) ,
145+ match opcode {
146+ Opcode :: BlackBoxFuncCall ( BlackBoxFuncCall :: RANGE { input } ) => {
147+ Some ( ( input. witness , input. num_bits ) )
148+ }
129149 _ => None ,
130150 }
131151}
@@ -246,4 +266,17 @@ mod tests {
246266 let ( optimized_circuit, _) = optimizer. replace_redundant_ranges ( acir_opcode_positions) ;
247267 assert_eq ! ( optimized_circuit. opcodes. len( ) , 5 ) ;
248268 }
269+
270+ #[ test]
271+ fn constant_implied_ranges ( ) {
272+ // The optimizer should use knowledge about constant witness assignments to remove range opcodes.
273+ let mut circuit = test_circuit ( vec ! [ ( Witness ( 1 ) , 16 ) ] ) ;
274+
275+ circuit. opcodes . push ( Opcode :: AssertZero ( Witness ( 1 ) . into ( ) ) ) ;
276+ let acir_opcode_positions = circuit. opcodes . iter ( ) . enumerate ( ) . map ( |( i, _) | i) . collect ( ) ;
277+ let optimizer = RangeOptimizer :: new ( circuit) ;
278+ let ( optimized_circuit, _) = optimizer. replace_redundant_ranges ( acir_opcode_positions) ;
279+ assert_eq ! ( optimized_circuit. opcodes. len( ) , 1 ) ;
280+ assert_eq ! ( optimized_circuit. opcodes[ 0 ] , Opcode :: AssertZero ( Witness ( 1 ) . into( ) ) ) ;
281+ }
249282}
0 commit comments