1
+ use std:: collections:: HashMap ;
2
+
1
3
use itertools:: Itertools ;
2
4
use triton_vm:: prelude:: * ;
3
5
4
6
use crate :: data_type:: ArrayType ;
5
7
use crate :: prelude:: * ;
8
+ use crate :: traits:: basic_snippet:: Reviewer ;
9
+ use crate :: traits:: basic_snippet:: SignOffFingerprint ;
6
10
7
11
/// Evaluate a polynomial in a point using the Horner method.
8
12
///
9
- /// HornerEvaluation takes an array of coefficients (representing a polynomial)
10
- /// and a scalar (representing an indeterminate) and computes the value of the
11
- /// polynomial in that point. It can be used for univariate batching, whereby
12
- /// the object is to compute a random linear sum of a given set of points, and
13
- /// the weights are given by the powers of one challenge.
13
+ /// `HornerEvaluation` takes an array of coefficients (representing a
14
+ /// polynomial) and a scalar (representing an indeterminate) and computes the
15
+ /// value of the polynomial in that point. It can be used for univariate
16
+ /// batching, whereby the object is to compute a random linear sum of a given
17
+ /// set of points, and the weights are given by the powers of one challenge.
18
+ ///
19
+ /// ### Behavior
20
+ ///
21
+ /// ```text
22
+ /// BEFORE: _ *coefficients [indeterminate: XFieldElement]
23
+ /// AFTER: _ [evaluation: XFieldElement]
24
+ /// ```
25
+ ///
26
+ /// ### Preconditions
27
+ ///
28
+ /// None.
29
+ ///
30
+ /// ### Postconditions
31
+ ///
32
+ /// None.
14
33
#[ derive( Debug , Copy , Clone , Eq , PartialEq , Hash ) ]
15
34
pub struct HornerEvaluation {
16
35
pub num_coefficients : usize ,
@@ -24,164 +43,121 @@ impl HornerEvaluation {
24
43
25
44
impl BasicSnippet for HornerEvaluation {
26
45
fn inputs ( & self ) -> Vec < ( DataType , String ) > {
46
+ let coefficients_ty = DataType :: Array ( Box :: new ( ArrayType {
47
+ element_type : DataType :: Xfe ,
48
+ length : self . num_coefficients ,
49
+ } ) ) ;
50
+
27
51
vec ! [
28
- (
29
- DataType :: Array ( Box :: new( ArrayType {
30
- element_type: DataType :: Xfe ,
31
- length: self . num_coefficients,
32
- } ) ) ,
33
- "*coefficients" . to_string( ) ,
34
- ) ,
52
+ ( coefficients_ty, "*coefficients" . to_string( ) ) ,
35
53
( DataType :: Xfe , "indeterminate" . to_string( ) ) ,
36
54
]
37
55
}
38
56
39
57
fn outputs ( & self ) -> Vec < ( DataType , String ) > {
40
- vec ! [ ( DataType :: Xfe , "value " . to_string( ) ) ]
58
+ vec ! [ ( DataType :: Xfe , "evaluation " . to_string( ) ) ]
41
59
}
42
60
43
61
fn entrypoint ( & self ) -> String {
44
- format ! (
45
- "tasmlib_array_horner_evaluation_with_{}_coefficients" ,
46
- self . num_coefficients
47
- )
62
+ let n = self . num_coefficients ;
63
+ format ! ( "tasmlib_array_horner_evaluation_with_{n}_coefficients" )
48
64
}
49
65
50
- fn code ( & self , _library : & mut Library ) -> Vec < LabelledInstruction > {
51
- let entrypoint = self . entrypoint ( ) ;
52
-
66
+ fn code ( & self , _: & mut Library ) -> Vec < LabelledInstruction > {
53
67
let update_running_evaluation = triton_asm ! {
54
- // BEFORE: _ *coefficients_end [x ] [v]
55
- // AFTER : _ [ vx+c]
68
+ // BEFORE: _ *coefficients_end [x: XFE ] [v: XFE ]
69
+ // AFTER : _ *coefficients_end-3 [x: XFE] [ vx+c: XFE ]
56
70
dup 5 dup 5 dup 5 // _ *coefficients_end [x] [v] [x]
57
- xx_mul // _ *coefficients_end [x] [vx]
58
- dup 6 // _ *coefficients_end [x] [vx] *coefficients_end
59
- read_mem 3 // _ *coefficients_end [x] [vx] [c] *coefficients_end+3
60
- swap 10 // _ *coefficients_end-3 [x] [vx] [c] *coefficients_end
61
- pop 1 // _ *coefficients_end-3 [x] [vx] [c]
62
- xx_add // _ *coefficients_end-3 [x] [vc+c]
71
+ xx_mul // _ *coefficients_end [x] [vx]
72
+ pick 6 // _ [x] [vx] *coefficients_end
73
+ read_mem 3 // _ [x] [vx] [c] *coefficients_end-3
74
+ place 9 // _ *coefficients_end-3 [x] [vx] [c]
75
+ xx_add // _ *coefficients_end-3 [x] [vx+c]
63
76
} ;
64
-
65
77
let update_running_evaluation_for_each_coefficient = ( 0 ..self . num_coefficients )
66
78
. flat_map ( |_| update_running_evaluation. clone ( ) )
67
79
. collect_vec ( ) ;
68
80
69
- let jump_to_end = self . num_coefficients as isize * 3 - 1 ;
70
-
71
81
triton_asm ! {
72
- // BEFORE: _ *coefficients x2 x1 x0
73
- // AFTER: _ v2 v1 v0
74
- { entrypoint} :
75
- // point to the last element of the array
76
- swap 3
77
- push { jump_to_end} add
78
- swap 3
79
-
80
- // push initial value of running evaluation
81
- push 0 push 0 push 0 // _ *coefficients_end [x] [v]
82
-
83
- // update running evaluation {num_coefficients_end}-many times
82
+ // BEFORE: _ *coefficients [x: XFE]
83
+ // AFTER: _ [v: XFE]
84
+ { self . entrypoint( ) } :
85
+ pick 3 // _ [x: XFE] *coefficients
86
+ addi { ( self . num_coefficients * 3 ) . saturating_sub( 1 ) }
87
+ place 3 // _ *coefficients_end [x: XFE]
88
+ push 0 push 0 push 0 // _ *coefficients_end [x: XFE] [v: XFE]
84
89
{ & update_running_evaluation_for_each_coefficient}
85
- // _ *coefficients_end-3n [x] [v']
86
-
87
- // clean up stack
88
- // _ *coefficients_end-3n x2 x1 x0 v2 v1 v0
89
- swap 4 pop 1 // _ *coefficients_end-3n x2 v0 x0 v2 v1
90
- swap 4 pop 1 // _ *coefficients_end-3n v1 v0 x0 v2
91
- swap 4 pop 1 // _ v2 v1 v0 x0
92
- pop 1 // _ v2 v1 v0
90
+ // _ *coefficients_end-3n [x: XFE] [v': XFE]
91
+ place 6 place 6 place 6 // _ [v': XFE] *coefficients_end-3n [x: XFE]
92
+ pop 4 // _ [v': XFE]
93
93
return
94
94
}
95
95
}
96
+
97
+ fn sign_offs ( & self ) -> HashMap < Reviewer , SignOffFingerprint > {
98
+ let mut sign_offs = HashMap :: new ( ) ;
99
+ if self . num_coefficients == 4 {
100
+ sign_offs. insert ( Reviewer ( "ferdinand" ) , 0xec460e65f9c22a87 . into ( ) ) ;
101
+ }
102
+
103
+ sign_offs
104
+ }
96
105
}
97
106
98
107
#[ cfg( test) ]
99
108
mod tests {
100
- use num:: Zero ;
101
-
102
109
use super :: * ;
103
- use crate :: empty_stack;
104
110
use crate :: rust_shadowing_helper_functions:: array:: array_get;
105
111
use crate :: rust_shadowing_helper_functions:: array:: insert_as_array;
106
112
use crate :: test_prelude:: * ;
113
+ use crate :: twenty_first:: prelude:: Polynomial ;
107
114
108
- impl Function for HornerEvaluation {
115
+ impl Accessor for HornerEvaluation {
109
116
fn rust_shadow (
110
117
& self ,
111
118
stack : & mut Vec < BFieldElement > ,
112
- memory : & mut HashMap < BFieldElement , BFieldElement > ,
119
+ memory : & HashMap < BFieldElement , BFieldElement > ,
113
120
) {
114
- // read indeterminate
115
- let x = XFieldElement :: new ( [
116
- stack. pop ( ) . unwrap ( ) ,
117
- stack. pop ( ) . unwrap ( ) ,
118
- stack. pop ( ) . unwrap ( ) ,
119
- ] ) ;
120
-
121
- // read location of array
122
- let pointer = stack. pop ( ) . unwrap ( ) ;
121
+ let indeterminate = pop_encodable ( stack) ;
122
+ let coefficient_ptr = stack. pop ( ) . unwrap ( ) ;
123
123
124
- // read array of coefficients
125
124
let coefficients = ( 0 ..self . num_coefficients )
126
- . map ( |i| array_get ( pointer , i, memory, 3 ) )
125
+ . map ( |i| array_get ( coefficient_ptr , i, memory, 3 ) )
127
126
. map ( |bfes| XFieldElement :: new ( bfes. try_into ( ) . unwrap ( ) ) )
128
- . collect_vec ( ) ;
129
-
130
- // evaluate polynomial using Horner's method
131
- let mut running_evaluation = XFieldElement :: zero ( ) ;
132
- for c in coefficients. into_iter ( ) . rev ( ) {
133
- running_evaluation *= x;
134
- running_evaluation += c;
135
- }
136
-
137
- // push value to stack
138
- let mut value = running_evaluation. coefficients . to_vec ( ) ;
139
- stack. push ( value. pop ( ) . unwrap ( ) ) ;
140
- stack. push ( value. pop ( ) . unwrap ( ) ) ;
141
- stack. push ( value. pop ( ) . unwrap ( ) ) ;
127
+ . collect ( ) ;
128
+ let polynomial = Polynomial :: new ( coefficients) ;
129
+ let evaluation = polynomial. evaluate_in_same_field ( indeterminate) ;
130
+
131
+ push_encodable ( stack, & evaluation) ;
142
132
}
143
133
144
134
fn pseudorandom_initial_state (
145
135
& self ,
146
136
seed : [ u8 ; 32 ] ,
147
- _bench_case : Option < BenchmarkCase > ,
148
- ) -> FunctionInitialState {
137
+ _ : Option < BenchmarkCase > ,
138
+ ) -> AccessorInitialState {
149
139
let mut rng = StdRng :: from_seed ( seed) ;
150
140
151
- // sample coefficients
152
141
let coefficients = ( 0 ..self . num_coefficients )
153
142
. map ( |_| rng. gen :: < XFieldElement > ( ) )
154
- . collect_vec ( ) ;
143
+ . collect ( ) ;
144
+ let address = rng. gen ( ) ;
155
145
156
- // sample address
157
- let address = BFieldElement :: new ( rng. next_u64 ( ) % ( 1 << 30 ) ) ;
158
- println ! ( "address: {}" , address. value( ) ) ;
159
-
160
- // store coefficients
161
- let mut memory: HashMap < BFieldElement , BFieldElement > = HashMap :: new ( ) ;
146
+ let mut memory = HashMap :: new ( ) ;
162
147
insert_as_array ( address, & mut memory, coefficients) ;
163
148
164
- // sample indeterminate
165
- let x: XFieldElement = rng. gen ( ) ;
166
-
167
- // prepare stack
168
- let mut stack = empty_stack ( ) ;
149
+ let mut stack = self . init_stack_for_isolated_run ( ) ;
169
150
stack. push ( address) ;
170
- stack. push ( x. coefficients [ 2 ] ) ;
171
- stack. push ( x. coefficients [ 1 ] ) ;
172
- stack. push ( x. coefficients [ 0 ] ) ;
151
+ push_encodable ( & mut stack, & rng. gen :: < XFieldElement > ( ) ) ; // indeterminate
173
152
174
- FunctionInitialState { stack, memory }
153
+ AccessorInitialState { stack, memory }
175
154
}
176
155
}
177
156
178
157
#[ test]
179
- fn horner_evaluation ( ) {
180
- for n in [ 0 , 1 , 20 , 587 , 1000 ] {
181
- let horner = HornerEvaluation {
182
- num_coefficients : n,
183
- } ;
184
- ShadowedFunction :: new ( horner) . test ( ) ;
158
+ fn rust_shadow ( ) {
159
+ for n in [ 0 , 1 , 4 , 20 , 587 , 1000 ] {
160
+ ShadowedAccessor :: new ( HornerEvaluation :: new ( n) ) . test ( ) ;
185
161
}
186
162
}
187
163
}
@@ -192,14 +168,9 @@ mod benches {
192
168
use crate :: test_prelude:: * ;
193
169
194
170
#[ test]
195
- fn bench_100 ( ) {
196
- let num_coefficients = 100 ;
197
- ShadowedFunction :: new ( HornerEvaluation { num_coefficients } ) . bench ( ) ;
198
- }
199
-
200
- #[ test]
201
- fn bench_587 ( ) {
202
- let num_coefficients = 587 ;
203
- ShadowedFunction :: new ( HornerEvaluation { num_coefficients } ) . bench ( ) ;
171
+ fn bench ( ) {
172
+ for n in [ 100 , 587 ] {
173
+ ShadowedAccessor :: new ( HornerEvaluation :: new ( n) ) . bench ( ) ;
174
+ }
204
175
}
205
176
}
0 commit comments