1use std::sync::Arc;
2
3use getset::Getters;
4use openvm_cuda_common::{
5 copy::{MemCopyD2D, MemCopyH2D},
6 d_buffer::DeviceBuffer,
7 error::CudaError,
8 stream::{cudaStream_t, GpuDeviceCtx},
9};
10use openvm_stark_backend::prover::MatrixDimensions;
11use p3_field::PrimeCharacteristicRing;
12
13use crate::{
14 base::DeviceMatrix,
15 cuda::{
16 batch_ntt_small::{batch_ntt_small, validate_gpu_l_skip},
17 mle_interpolate::{
18 mle_interpolate_fused_2d, mle_interpolate_shared_2d, mle_interpolate_stage_2d,
19 MLE_SHARED_TILE_LOG_SIZE,
20 },
21 poly::{
22 eq_hypercube_interleaved_stage_ext, eq_hypercube_nonoverlapping_stage_ext,
23 eq_hypercube_stage_ext, mobius_eq_hypercube_stage_ext,
24 },
25 sumcheck::fold_mle_column,
26 LOG_WARP_SIZE,
27 },
28 prelude::{EF, F},
29 KernelError,
30};
31
32#[derive(derive_new::new, Getters)]
33pub struct PleMatrix<F> {
34 #[getset(get = "pub")]
37 pub(crate) mixed: DeviceBuffer<F>,
38 height: usize,
39 width: usize,
40}
41
42impl<F> MatrixDimensions for PleMatrix<F> {
43 fn width(&self) -> usize {
44 self.width
45 }
46
47 fn height(&self) -> usize {
48 self.height
49 }
50}
51
52impl PleMatrix<F> {
53 pub fn from_evals(
55 l_skip: usize,
56 evals: DeviceBuffer<F>,
57 height: usize,
58 width: usize,
59 device_ctx: &GpuDeviceCtx,
60 ) -> Self {
61 validate_gpu_l_skip(l_skip).expect("GPU PleMatrix requires l_skip <= 9");
62 let mut mixed = evals;
63 if l_skip > 0 {
64 let num_uni_poly = width * (height >> l_skip);
67 unsafe {
68 batch_ntt_small(
69 &mut mixed,
70 l_skip,
71 num_uni_poly,
72 true,
73 device_ctx.stream.as_raw(),
74 )
75 .unwrap();
76 }
77 }
78 Self {
79 mixed,
80 height,
81 width,
82 }
83 }
84
85 pub fn to_evals(
86 &self,
87 l_skip: usize,
88 device_ctx: &GpuDeviceCtx,
89 ) -> Result<DeviceMatrix<F>, KernelError> {
90 validate_gpu_l_skip(l_skip)?;
91 let width = self.width();
92 let height = self.height();
93 let mut evals = self.mixed.device_copy_on(device_ctx)?;
95 if l_skip > 0 {
96 let num_uni_poly = width * (height >> l_skip);
99 unsafe {
100 batch_ntt_small(
101 &mut evals,
102 l_skip,
103 num_uni_poly,
104 false,
105 device_ctx.stream.as_raw(),
106 )
107 .unwrap();
108 }
109 }
110 Ok(DeviceMatrix::new(Arc::new(evals), height, width))
111 }
112}
113
114pub fn mle_evals_to_coeffs_inplace(
117 evals: &mut DeviceBuffer<F>,
118 n: usize,
119 device_ctx: &GpuDeviceCtx,
120) -> Result<(), CudaError> {
121 if n == 0 {
122 return Ok(());
123 }
124 debug_assert!(evals.len().is_multiple_of(1 << n));
125 let width = evals.len() >> n;
126 unsafe {
130 mle_interpolate_stages(
131 evals.as_mut_ptr(),
132 width,
133 1 << n,
134 0,
135 0,
136 n as u32 - 1,
137 true,
138 false,
139 device_ctx.stream.as_raw(),
140 )
141 }
142}
143
144#[allow(clippy::too_many_arguments)]
162pub unsafe fn mle_interpolate_stages(
163 buffer: *mut F,
164 width: usize,
165 padded_height: u32,
166 log_blowup: u32,
167 start_log_step: u32,
168 end_log_step: u32,
169 is_eval_to_coeff: bool,
170 right_pad: bool,
171 stream: cudaStream_t,
172) -> Result<(), CudaError> {
173 if start_log_step > end_log_step {
174 return Ok(());
175 }
176
177 let mut current_log_step = start_log_step;
178
179 let warp_end = end_log_step.min(LOG_WARP_SIZE as u32 - 1);
182 let warp_stages = warp_end.saturating_sub(current_log_step) + 1;
183 if current_log_step < LOG_WARP_SIZE as u32 && warp_stages >= 2 {
184 let num_stages = warp_stages;
185 let start_step = 1u32 << current_log_step;
186
187 mle_interpolate_fused_2d(
188 buffer,
189 width,
190 padded_height,
191 log_blowup,
192 start_step,
193 num_stages,
194 is_eval_to_coeff,
195 right_pad,
196 stream,
197 )?;
198
199 current_log_step = warp_end + 1;
200 }
201
202 if current_log_step > end_log_step {
203 return Ok(());
204 }
205
206 if current_log_step < MLE_SHARED_TILE_LOG_SIZE {
208 let shared_end = end_log_step.min(MLE_SHARED_TILE_LOG_SIZE - 1);
209
210 mle_interpolate_shared_2d(
211 buffer,
212 width,
213 padded_height,
214 log_blowup,
215 current_log_step,
216 shared_end,
217 is_eval_to_coeff,
218 right_pad,
219 stream,
220 )?;
221
222 current_log_step = shared_end + 1;
223 }
224
225 assert!(
228 current_log_step > end_log_step || !right_pad,
229 "bit_reversed mode not supported for log_step >= MLE_SHARED_TILE_LOG_SIZE"
230 );
231 let height = padded_height >> log_blowup;
232 while current_log_step <= end_log_step {
233 let step = 1u32 << current_log_step;
234 mle_interpolate_stage_2d(
235 buffer,
236 width,
237 height,
238 padded_height,
239 step,
240 is_eval_to_coeff,
241 stream,
242 )?;
243 current_log_step += 1;
244 }
245
246 Ok(())
247}
248
249pub unsafe fn evals_eq_hypercube(
262 out: &mut DeviceBuffer<EF>,
263 xs: &[EF],
264 device_ctx: &GpuDeviceCtx,
265) -> Result<(), KernelError> {
266 let n = xs.len();
267 assert!(out.len() >= 1 << n);
268 [EF::ONE]
270 .copy_to_on(out, device_ctx)
271 .map_err(KernelError::MemCopy)?;
272
273 for (i, &x_i) in xs.iter().enumerate() {
274 let step = 1 << i;
275 eq_hypercube_stage_ext(out.as_mut_ptr(), x_i, step, device_ctx.stream.as_raw())
276 .map_err(KernelError::Kernel)?;
277 }
278 Ok(())
279}
280
281pub unsafe fn evals_mobius_eq_hypercube(
295 out: &mut DeviceBuffer<EF>,
296 omega: &[EF],
297 device_ctx: &GpuDeviceCtx,
298) -> Result<(), KernelError> {
299 let n = omega.len();
300 assert!(out.len() >= 1 << n);
301 [EF::ONE]
303 .copy_to_on(out, device_ctx)
304 .map_err(KernelError::MemCopy)?;
305
306 for (i, &omega_i) in omega.iter().enumerate() {
307 let step = 1 << i;
308 mobius_eq_hypercube_stage_ext(out.as_mut_ptr(), omega_i, step, device_ctx.stream.as_raw())
309 .map_err(KernelError::Kernel)?;
310 }
311 Ok(())
312}
313
314#[derive(Getters)]
318pub struct EqEvalSegments<F> {
319 #[getset(get = "pub")]
321 pub(crate) buffer: DeviceBuffer<F>,
322 #[getset(get_copy = "pub")]
323 max_n: usize,
324}
325
326impl<F> EqEvalSegments<F> {
327 pub fn get_ptr(&self, n: usize) -> *const F {
330 assert!(n <= self.max_n);
331 unsafe { self.buffer.as_ptr().add(1 << n) }
333 }
334
335 pub unsafe fn from_raw_parts(buffer: DeviceBuffer<F>, max_n: usize) -> Self {
338 Self { buffer, max_n }
339 }
340}
341
342impl EqEvalSegments<EF> {
344 pub fn new(x: &[EF], device_ctx: &GpuDeviceCtx) -> Result<Self, KernelError> {
346 let max_n = x.len();
347 let mut buffer = DeviceBuffer::with_capacity_on(2 << max_n, device_ctx);
348 [EF::ZERO, EF::ONE]
351 .copy_to_on(&mut buffer, device_ctx)
352 .map_err(KernelError::MemCopy)?;
353 for (i, &x_i) in x.iter().enumerate() {
355 let step = 1 << i;
356 unsafe {
360 let dst = buffer.as_mut_ptr().add(2 * step);
362 let src = buffer.as_ptr().add(step);
364 eq_hypercube_nonoverlapping_stage_ext(
365 dst,
366 src,
367 x_i,
368 step as u32,
369 device_ctx.stream.as_raw(),
370 )
371 .map_err(KernelError::Kernel)?;
372 }
373 }
374 Ok(Self { buffer, max_n })
375 }
376}
377
378#[derive(Getters)]
381pub struct EqEvalLayers<F> {
382 pub layers: Vec<Arc<DeviceBuffer<F>>>,
384}
385
386impl<F> EqEvalLayers<F> {
387 pub fn get_ptr(&self, n: usize) -> *const F {
390 debug_assert_eq!(self.layers[n].len(), 1 << n);
391 self.layers[n].as_ptr()
392 }
393}
394
395impl EqEvalLayers<EF> {
397 pub(crate) fn one_layer(
398 device_ctx: &GpuDeviceCtx,
399 ) -> Result<Arc<DeviceBuffer<EF>>, KernelError> {
400 [EF::ONE]
401 .to_device_on(device_ctx)
402 .map(Arc::new)
403 .map_err(KernelError::MemCopy)
404 }
405
406 pub fn new_rev<'a>(
410 n: usize,
411 x: impl IntoIterator<Item = &'a EF>,
412 device_ctx: &GpuDeviceCtx,
413 ) -> Result<Self, KernelError> {
414 Self::new_rev_with_one(n, x, Self::one_layer(device_ctx)?, device_ctx)
415 }
416
417 pub(crate) fn new_rev_with_one<'a>(
418 n: usize,
419 x: impl IntoIterator<Item = &'a EF>,
420 layer_0: Arc<DeviceBuffer<EF>>,
421 device_ctx: &GpuDeviceCtx,
422 ) -> Result<Self, KernelError> {
423 let mut layers = Vec::with_capacity(n + 1);
424 layers.push(layer_0);
425 for (i, &x_i) in x.into_iter().enumerate() {
426 let step = 1 << i;
427 let buffer = DeviceBuffer::with_capacity_on(2 * step, device_ctx);
428 unsafe {
431 let dst = buffer.as_mut_ptr();
432 let src = layers.last().unwrap().as_ptr();
433 eq_hypercube_interleaved_stage_ext(
434 dst,
435 src,
436 x_i,
437 step as u32,
438 device_ctx.stream.as_raw(),
439 )
440 .map_err(KernelError::Kernel)?;
441 }
442 layers.push(Arc::new(buffer));
443 }
444 Ok(Self { layers })
445 }
446
447 pub fn new<'a>(
452 n: usize,
453 x: impl IntoIterator<Item = &'a EF>,
454 device_ctx: &GpuDeviceCtx,
455 ) -> Result<Self, KernelError> {
456 Self::new_with_one(n, x, Self::one_layer(device_ctx)?, device_ctx)
457 }
458
459 pub(crate) fn new_with_one<'a>(
460 n: usize,
461 x: impl IntoIterator<Item = &'a EF>,
462 layer_0: Arc<DeviceBuffer<EF>>,
463 device_ctx: &GpuDeviceCtx,
464 ) -> Result<Self, KernelError> {
465 let mut layers = Vec::with_capacity(n + 1);
466 layers.push(layer_0);
467 for (i, &x_i) in x.into_iter().enumerate() {
468 let step = 1 << i;
469 let buffer = DeviceBuffer::with_capacity_on(2 * step, device_ctx);
470 unsafe {
473 let dst = buffer.as_mut_ptr();
474 let src = layers.last().unwrap().as_ptr();
475 eq_hypercube_nonoverlapping_stage_ext(
476 dst,
477 src,
478 x_i,
479 step as u32,
480 device_ctx.stream.as_raw(),
481 )
482 .map_err(KernelError::Kernel)?;
483 }
484 layers.push(Arc::new(buffer));
485 }
486 Ok(Self { layers })
487 }
488}
489
490pub struct SqrtHyperBuffer {
496 pub low: DeviceBuffer<EF>,
497 pub high: DeviceBuffer<EF>,
498 pub low_capacity: usize,
499 pub size: usize,
500}
501
502impl SqrtHyperBuffer {
503 pub fn from_xi(xi: &[EF], device_ctx: &GpuDeviceCtx) -> Result<Self, KernelError> {
506 let low = {
507 let mut res = DeviceBuffer::with_capacity_on(1 << (xi.len() / 2), device_ctx);
508 unsafe { evals_eq_hypercube(&mut res, &xi[..xi.len() / 2], device_ctx)? };
509 res
510 };
511 let high = {
512 let mut res = DeviceBuffer::with_capacity_on(1 << xi.len().div_ceil(2), device_ctx);
513 unsafe { evals_eq_hypercube(&mut res, &xi[xi.len() / 2..], device_ctx)? };
514 res
515 };
516 Ok(Self {
517 low,
518 high,
519 low_capacity: 1 << (xi.len() / 2),
520 size: 1 << xi.len(),
521 })
522 }
523
524 pub fn fold_columns(&mut self, r: EF, device_ctx: &GpuDeviceCtx) -> Result<(), CudaError> {
525 assert!(self.size > 1);
526 if self.size > self.low_capacity {
527 unsafe {
528 fold_mle_column(
529 &mut self.high,
530 self.size / self.low_capacity,
531 r,
532 device_ctx.stream.as_raw(),
533 )?;
534 }
535 } else {
536 unsafe {
537 fold_mle_column(&mut self.low, self.size, r, device_ctx.stream.as_raw())?;
538 }
539 };
540 self.size /= 2;
541 Ok(())
542 }
543}
544
545pub struct SqrtEqLayers {
550 pub low: EqEvalLayers<EF>,
553 pub high: EqEvalLayers<EF>,
556}
557
558impl SqrtEqLayers {
559 pub fn from_xi(xi: &[EF], device_ctx: &GpuDeviceCtx) -> Result<Self, KernelError> {
565 let n = xi.len();
566 let low_n = n / 2;
567 let high_n = n - low_n;
568
569 let layer_0 = EqEvalLayers::one_layer(device_ctx)?;
570 let low = EqEvalLayers::new_with_one(
571 low_n,
572 xi[high_n..].iter().rev(),
573 Arc::clone(&layer_0),
574 device_ctx,
575 )?;
576 let high =
577 EqEvalLayers::new_with_one(high_n, xi[..high_n].iter().rev(), layer_0, device_ctx)?;
578
579 Ok(Self { low, high })
580 }
581
582 pub fn max_n(&self) -> usize {
583 self.low_n() + self.high_n()
584 }
585
586 pub fn low_n(&self) -> usize {
587 self.low.layers.len() - 1
588 }
589
590 pub fn high_n(&self) -> usize {
591 self.high.layers.len() - 1
592 }
593
594 pub fn drop_layer(&mut self) {
597 if self.high.layers.len() > 1 {
598 self.high.layers.pop();
599 } else if self.low.layers.len() > 1 {
600 self.low.layers.pop();
601 }
602 }
603}