#![allow(dead_code)]
use openvm_cuda_common::{
copy::{MemCopyD2H, MemCopyH2D},
d_buffer::DeviceBuffer,
stream::GpuDeviceCtx,
};
use openvm_stark_backend::{
p3_util::log2_strict_usize,
poly_common::UnivariatePoly,
prover::sumcheck::{SumcheckCubeProof, SumcheckPrismProof},
FiatShamirTranscript,
};
use p3_field::{ExtensionField, Field};
use tracing::{debug, info_span, instrument};
use crate::{
cuda::{
batch_ntt_small::batch_ntt_small,
matrix::batch_expand_pad_wide,
sumcheck::{fold_mle, fold_ple_from_coeffs, reduce_over_x_and_cols, sumcheck_mle_round},
},
error::SumcheckError,
prelude::*,
sponge::DuplexSpongeGpu,
};
#[allow(dead_code)]
#[instrument(name = "sumcheck_multilinear_gpu", level = "info", skip_all)]
pub fn sumcheck_multilinear_gpu<F: Field>(
transcript: &mut DuplexSpongeGpu,
evals: &[F],
device_ctx: &GpuDeviceCtx,
) -> Result<(SumcheckCubeProof<EF>, Vec<EF>), SumcheckError>
where
EF: ExtensionField<F>,
{
let n = log2_strict_usize(evals.len());
let mut round_polys_eval = Vec::with_capacity(n);
let mut r = Vec::with_capacity(n);
let sum_claim: EF = evals.iter().copied().sum::<F>().into();
transcript.observe_ext(sum_claim);
let evals_ext: Vec<EF> = evals.iter().map(|&x| EF::from(x)).collect();
let mut current_height = 1 << n;
let width = 1;
let num_matrices = 1;
let d = 1; const WD: usize = 1;
let total_size = width * current_height;
let mut d_buffer_a = evals_ext.to_device_on(device_ctx)?;
let mut d_buffer_b = DeviceBuffer::<EF>::with_capacity_on(total_size / 2, device_ctx);
let mut d_input_ptrs = DeviceBuffer::<*const EF>::with_capacity_on(num_matrices, device_ctx);
let mut d_output_ptrs = DeviceBuffer::<*mut EF>::with_capacity_on(num_matrices, device_ctx);
let d_widths = [width as u32].to_device_on(device_ctx)?;
let d_round_output = DeviceBuffer::<EF>::with_capacity_on(d * WD, device_ctx);
for round in 0..n {
let (input_buf, output_buf) = if round % 2 == 0 {
(&d_buffer_a, &mut d_buffer_b)
} else {
(&d_buffer_b, &mut d_buffer_a)
};
let input_ptr = input_buf.as_ptr();
let output_ptr = output_buf.as_mut_ptr();
[input_ptr].copy_to_on(&mut d_input_ptrs, device_ctx)?;
[output_ptr].copy_to_on(&mut d_output_ptrs, device_ctx)?;
unsafe {
sumcheck_mle_round(
&d_input_ptrs,
&d_round_output,
output_buf, &d_widths,
num_matrices as u32,
current_height as u32,
d as u32,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::SumcheckMleRound(e.into()))?;
}
let h_round_output = d_round_output.to_host_on(device_ctx)?;
let s = h_round_output[0..d].to_vec();
assert_eq!(s.len(), d);
transcript.observe_ext(s[0]);
round_polys_eval.push(s);
let r_round = transcript.sample_ext();
debug!(%round, %r_round);
r.push(r_round);
let output_height = (current_height >> 1) as u32;
unsafe {
fold_mle(
&d_input_ptrs,
&d_output_ptrs,
&d_widths,
num_matrices.try_into().unwrap(),
output_height,
width as u32 * output_height,
r_round,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::FoldMle(e.into()))?;
}
current_height >>= 1;
}
let final_buf = if n % 2 == 1 { &d_buffer_b } else { &d_buffer_a };
let eval_claim_vec = final_buf.to_host_on(device_ctx)?;
let eval_claim = eval_claim_vec[0];
transcript.observe_ext(eval_claim);
Ok((
SumcheckCubeProof {
sum_claim,
round_polys_eval,
eval_claim,
},
r,
))
}
#[allow(dead_code)]
#[instrument(name = "sumcheck_prismalinear_gpu", level = "info", skip_all)]
pub fn sumcheck_prismalinear_gpu(
transcript: &mut DuplexSpongeGpu,
l_skip: usize,
evals: &[F],
device_ctx: &GpuDeviceCtx,
) -> Result<(SumcheckPrismProof<EF>, Vec<EF>), SumcheckError> {
let prism_dim = p3_util::log2_strict_usize(evals.len());
assert!(prism_dim >= l_skip);
let n = prism_dim - l_skip;
let mut round_polys_eval = Vec::with_capacity(n);
let mut r = Vec::with_capacity(n + 1);
let sum_claim: EF = evals.iter().copied().sum::<F>().into();
transcript.observe_ext(sum_claim);
let domain_size = 1 << l_skip;
let num_x = 1 << n;
let width = 1;
let d = 1; let s_deg = d * (domain_size - 1); let log_large_domain = p3_util::log2_ceil_usize(s_deg + 1);
let large_domain_size = 1 << log_large_domain;
let _round0_span = info_span!("sumcheck_prismalinear.round0").entered();
let mut d_coeffs = evals.to_device_on(device_ctx)?;
let mut d_s0_coeffs = DeviceBuffer::<F>::with_capacity_on(large_domain_size, device_ctx);
unsafe {
batch_ntt_small(
&mut d_coeffs,
l_skip,
num_x * width,
true,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::BatchNttSmall(e.into()))?;
}
if domain_size == large_domain_size {
unsafe {
reduce_over_x_and_cols(
&d_coeffs,
&d_s0_coeffs,
num_x as u32,
width as u32,
large_domain_size as u32,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::ReduceOverXAndCols(e.into()))?;
}
} else {
let mut d_coeffs_large =
DeviceBuffer::<F>::with_capacity_on(num_x * width * large_domain_size, device_ctx);
unsafe {
batch_expand_pad_wide(
d_coeffs_large.as_mut_ptr(),
d_coeffs.as_ptr(),
(num_x * width) as u32,
large_domain_size as u32,
domain_size as u32,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::BatchExpandPadWide(e.into()))?;
}
unsafe {
batch_ntt_small(
&mut d_coeffs_large,
log_large_domain,
num_x * width,
false,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::BatchNttSmall(e.into()))?;
}
unsafe {
reduce_over_x_and_cols(
&d_coeffs_large,
&d_s0_coeffs,
num_x as u32,
width as u32,
large_domain_size as u32,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::ReduceOverXAndCols(e.into()))?;
}
drop(d_coeffs_large);
unsafe {
batch_ntt_small(
&mut d_s0_coeffs,
log_large_domain,
1,
true,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::BatchNttSmall(e.into()))?;
}
}
let s0_coeffs_host: Vec<F> = d_s0_coeffs.to_host_on(device_ctx)?;
drop(d_s0_coeffs);
let s0_coeffs_ext: Vec<EF> = s0_coeffs_host[0..=s_deg]
.iter()
.map(|&x| EF::from(x))
.collect();
let s_0 = UnivariatePoly::new(s0_coeffs_ext.clone());
for &coeff in &s0_coeffs_ext {
transcript.observe_ext(coeff);
}
let r_0 = transcript.sample_ext();
debug!(round = 0, r_round = %r_0);
r.push(r_0);
let d_folded = DeviceBuffer::<EF>::with_capacity_on(num_x, device_ctx);
unsafe {
fold_ple_from_coeffs(
d_coeffs.as_ptr(), d_folded.as_mut_ptr(), num_x as u32,
width as u32,
domain_size as u32,
r_0,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::FoldPleFromCoeffs(e.into()))?;
}
drop(d_coeffs);
drop(_round0_span);
let _mle_rounds_span = info_span!("sumcheck_prismalinear.mle_rounds").entered();
let mut current_height = num_x; let num_matrices = 1;
let mut d_buffer_a = d_folded; let mut d_buffer_b = DeviceBuffer::<EF>::with_capacity_on(current_height / 2, device_ctx);
let mut d_input_ptrs = DeviceBuffer::<*const EF>::with_capacity_on(num_matrices, device_ctx);
let mut d_output_ptrs = DeviceBuffer::<*mut EF>::with_capacity_on(num_matrices, device_ctx);
let d_widths = [width as u32].to_device_on(device_ctx)?;
let d_round_output = DeviceBuffer::<EF>::with_capacity_on(d, device_ctx);
for round in 1..=n {
let (input_buf, output_buf) = if round % 2 == 1 {
(&d_buffer_a, &mut d_buffer_b)
} else {
(&d_buffer_b, &mut d_buffer_a)
};
let input_ptr = input_buf.as_ptr();
let output_ptr = output_buf.as_mut_ptr();
[input_ptr].copy_to_on(&mut d_input_ptrs, device_ctx)?;
[output_ptr].copy_to_on(&mut d_output_ptrs, device_ctx)?;
unsafe {
sumcheck_mle_round(
&d_input_ptrs,
&d_round_output,
output_buf,
&d_widths,
num_matrices as u32,
current_height as u32,
d as u32,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::SumcheckMleRound(e.into()))?;
}
let h_round_output = d_round_output.to_host_on(device_ctx)?;
let s = h_round_output[0..d].to_vec();
assert_eq!(s.len(), d);
transcript.observe_ext(s[0]);
round_polys_eval.push(s);
let r_round = transcript.sample_ext();
debug!(%round, %r_round);
r.push(r_round);
let output_height = (current_height >> 1) as u32;
unsafe {
fold_mle(
&d_input_ptrs,
&d_output_ptrs,
&d_widths,
num_matrices.try_into().unwrap(),
output_height,
width as u32 * output_height,
r_round,
device_ctx.stream.as_raw(),
)
.map_err(|e| SumcheckError::FoldMle(e.into()))?;
}
current_height >>= 1;
}
drop(_mle_rounds_span);
let final_buf = if n % 2 == 1 { &d_buffer_b } else { &d_buffer_a };
let eval_claim_vec = final_buf.to_host_on(device_ctx)?;
let eval_claim = eval_claim_vec[0];
transcript.observe_ext(eval_claim);
Ok((
SumcheckPrismProof {
sum_claim,
s_0,
round_polys_eval,
eval_claim,
},
r,
))
}