tasm_lib/verifier/fri/
barycentric_evaluation.rs1use triton_vm::prelude::*;
2use twenty_first::math::x_field_element::EXTENSION_DEGREE;
3
4use crate::arithmetic::bfe::primitive_root_of_unity::PrimitiveRootOfUnity;
5use crate::prelude::*;
6
7const MAX_CODEWORD_LENGTH: u32 = 1 << 15;
8
9#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
13pub struct BarycentricEvaluation;
14
15impl BasicSnippet for BarycentricEvaluation {
16 fn inputs(&self) -> Vec<(DataType, String)> {
17 vec![
18 (
19 DataType::List(Box::new(DataType::Xfe)),
20 "codeword".to_owned(),
21 ),
22 (DataType::Xfe, "indeterminate".to_owned()),
23 ]
24 }
25
26 fn outputs(&self) -> Vec<(DataType, String)> {
27 vec![(DataType::Xfe, "evaluation_result".to_owned())]
28 }
29
30 fn entrypoint(&self) -> String {
31 "tasmlib_verifier_fri_barycentric_evaluation".to_owned()
32 }
33
34 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
35 let entrypoint = self.entrypoint();
36 let generator = library.import(Box::new(PrimitiveRootOfUnity));
37 let partial_terms_alloc = library.kmalloc(MAX_CODEWORD_LENGTH * EXTENSION_DEGREE as u32);
38
39 let calculate_and_store_partial_terms_loop_label =
41 format!("{entrypoint}_partial_terms_loop");
42 let calculate_and_store_partial_terms_code = triton_asm!(
43 {calculate_and_store_partial_terms_loop_label}:
46 dup 3
47 dup 3
48 dup 3
49 dup 3
50 add
51 x_invert
54 dup 3
57 xb_mul
58 pick 8
61 write_mem {EXTENSION_DEGREE}
62 place 5
63 dup 4
66 mul
67 recurse_or_return
70 );
71
72 let numerator_from_partial_sums_loop_label =
75 format!("{entrypoint}_numerator_from_partial_sums");
76 let numerator_from_partial_sums_loop_code = triton_asm!(
77 {numerator_from_partial_sums_loop_label}:
80 pick 5
81 xx_dot_step
82 place 5
83 recurse_or_return
84 );
85
86 let denominator_from_partial_sums_loop_label =
88 format!("{entrypoint}_denominator_from_partial_sums");
89 let denominator_from_partial_sums_loop_code = triton_asm!(
90 {denominator_from_partial_sums_loop_label}:
93 pick 5
96 read_mem {EXTENSION_DEGREE}
97 place 8
98 xx_add
101 recurse_or_return
104 );
105
106 triton_asm!(
107 {entrypoint}:
108 push -1
111 xb_mul
112 hint neg_indeterminate = stack[0..3]
113 dup 3
117 read_mem 1
118 pop 1
119 push {MAX_CODEWORD_LENGTH + 1}
123 dup 1
124 lt
125 assert
126 push 0
129 dup 1
130 call {generator}
136 hint generator: BFieldElement = stack[0]
137 pick 1
140 push {EXTENSION_DEGREE}
143 mul
144 push {partial_terms_alloc.write_address()}
145 add
146 place 4
149 place 3
150 push {partial_terms_alloc.write_address()}
151 place 4
152 push 1
155 call {calculate_and_store_partial_terms_loop_label}
158 pop 5
161 pop 1
162 place 1
165 push {partial_terms_alloc.write_address()}
166 push 0
167 push 0 push 0 push 0
168 pick 5
171 addi 1
172 call {numerator_from_partial_sums_loop_label}
175 hint numerator: XFieldElement = stack[1..4]
176 pick 4
179 pick 5
180 pop 3
181 pick 3
184 addi -1
185 push 0
188 push 0
189 push 0
190 push 0
191 push 0
192 push {partial_terms_alloc.write_address() - bfe!(1)}
195 hint loop_end_condition = stack[0]
196 place 6
197 call {denominator_from_partial_sums_loop_label}
200 hint denominator: XFieldElement = stack[0..2]
201 place 6
204 place 6
205 place 6
206 pop 4
207 x_invert
210 xx_mul
211 return
215
216 {&calculate_and_store_partial_terms_code}
217 {&numerator_from_partial_sums_loop_code}
218 {&denominator_from_partial_sums_loop_code}
219 )
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use twenty_first::math::other::random_elements;
226 use twenty_first::math::polynomial::barycentric_evaluate;
227 use twenty_first::math::traits::PrimitiveRootOfUnity;
228
229 use super::*;
230 use crate::library::STATIC_MEMORY_FIRST_ADDRESS;
231 use crate::rust_shadowing_helper_functions::list::list_insert;
232 use crate::rust_shadowing_helper_functions::list::load_list_with_copy_elements;
233 use crate::test_prelude::*;
234
235 #[test]
236 fn barycentric_evaluation_pbt() {
237 ShadowedFunction::new(BarycentricEvaluation).test()
238 }
239
240 impl BarycentricEvaluation {
241 fn prepare_state(
242 &self,
243 codeword: Vec<XFieldElement>,
244 codeword_pointer: BFieldElement,
245 indeterminate: XFieldElement,
246 ) -> FunctionInitialState {
247 let mut memory = HashMap::default();
248 list_insert(codeword_pointer, codeword, &mut memory);
249
250 let mut stack = self.init_stack_for_isolated_run();
251 stack.push(codeword_pointer);
252
253 for word in indeterminate.coefficients.into_iter().rev() {
254 stack.push(word);
255 }
256
257 FunctionInitialState { stack, memory }
258 }
259 }
260
261 impl Function for BarycentricEvaluation {
262 fn rust_shadow(
263 &self,
264 stack: &mut Vec<BFieldElement>,
265 memory: &mut HashMap<BFieldElement, BFieldElement>,
266 ) {
267 let indeterminate = XFieldElement::new([
268 stack.pop().unwrap(),
269 stack.pop().unwrap(),
270 stack.pop().unwrap(),
271 ]);
272 let codeword_pointer = stack.pop().unwrap();
273 let codeword =
274 load_list_with_copy_elements::<EXTENSION_DEGREE>(codeword_pointer, memory);
275 let codeword_length: u32 = codeword.len().try_into().unwrap();
276 assert!(codeword_length <= MAX_CODEWORD_LENGTH);
277
278 let codeword: Vec<XFieldElement> = codeword.into_iter().map(|x| x.into()).collect_vec();
279 let result = barycentric_evaluate(&codeword, indeterminate);
280
281 let generator = BFieldElement::primitive_root_of_unity(codeword.len() as u64).unwrap();
283 let mut partial_terms_pointer = STATIC_MEMORY_FIRST_ADDRESS
284 - bfe!(MAX_CODEWORD_LENGTH * EXTENSION_DEGREE as u32 - 1);
285 let mut gen_acc = bfe!(1);
286 for _ in 0..codeword_length {
287 let n = gen_acc;
288 let d = gen_acc - indeterminate;
289 let term: XFieldElement = d.inverse() * n;
290 memory.insert(partial_terms_pointer, term.coefficients[0]);
291 partial_terms_pointer.increment();
292 memory.insert(partial_terms_pointer, term.coefficients[1]);
293 partial_terms_pointer.increment();
294 memory.insert(partial_terms_pointer, term.coefficients[2]);
295 partial_terms_pointer.increment();
296
297 gen_acc *= generator;
298 }
299
300 for word in result.coefficients.into_iter().rev() {
301 stack.push(word);
302 }
303 }
304
305 fn pseudorandom_initial_state(
306 &self,
307 seed: [u8; 32],
308 bench_case: Option<BenchmarkCase>,
309 ) -> FunctionInitialState {
310 let mut rng = StdRng::from_seed(seed);
311 let codeword_length = match bench_case {
312 Some(BenchmarkCase::CommonCase) => 256,
313 Some(BenchmarkCase::WorstCase) => 512,
314 None => 1 << rng.random_range(0..=14),
315 };
316
317 let codeword_pointer = rng.random_range(0..=(1u64 << 34));
318 let codeword_pointer = bfe!(codeword_pointer);
319 let indeterminate: XFieldElement = rng.random();
320 let codeword = random_elements(codeword_length);
321
322 self.prepare_state(codeword, codeword_pointer, indeterminate)
323 }
324
325 fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
326 let some_indeterminate = XFieldElement::new([bfe!(1555), bfe!(1556), bfe!(1557)]);
327 let some_codeword_pointer = bfe!(19191919);
328 let codeword_of_length_one =
329 self.prepare_state(xfe_vec![155], some_codeword_pointer, some_indeterminate);
330 let const_codeword_of_length_two = self.prepare_state(
331 xfe_vec![155, 155],
332 some_codeword_pointer,
333 some_indeterminate,
334 );
335 let non_const_codeword_of_length_two = self.prepare_state(
336 xfe_vec![155, 1_919_191_919],
337 some_codeword_pointer,
338 some_indeterminate,
339 );
340 let const_codeword_of_length_8 =
341 self.prepare_state(xfe_vec![155; 8], some_codeword_pointer, some_indeterminate);
342
343 vec![
344 codeword_of_length_one,
345 const_codeword_of_length_two,
346 non_const_codeword_of_length_two,
347 const_codeword_of_length_8,
348 ]
349 }
350 }
351}
352
353#[cfg(test)]
354mod benches {
355 use super::*;
356 use crate::test_prelude::*;
357
358 #[test]
359 fn bench_barycentric_evaluation() {
360 ShadowedFunction::new(BarycentricEvaluation).bench();
361 }
362}