openvm_stark_backend/prover/cpu/quotient/
mod.rs
1use std::sync::Arc;
2
3use itertools::{izip, multiunzip, Itertools};
4use p3_commit::{Pcs, PolynomialSpace};
5use p3_field::FieldAlgebra;
6use p3_matrix::{dense::RowMajorMatrix, Matrix};
7use p3_util::log2_strict_usize;
8use tracing::instrument;
9
10use self::single::compute_single_rap_quotient_values;
11use super::PcsData;
12use crate::{
13 air_builders::symbolic::SymbolicExpressionDag,
14 config::{Com, Domain, PackedChallenge, StarkGenericConfig, Val},
15 prover::types::RapView,
16};
17
18mod evaluator;
19pub(crate) mod single;
20
21pub struct QuotientCommitter<'pcs, SC: StarkGenericConfig> {
22 pcs: &'pcs SC::Pcs,
23 alpha: SC::Challenge,
24}
25
26impl<'pcs, SC: StarkGenericConfig> QuotientCommitter<'pcs, SC> {
27 pub fn new(pcs: &'pcs SC::Pcs, alpha: SC::Challenge) -> Self {
28 Self { pcs, alpha }
29 }
30
31 #[instrument(name = "compute quotient values", level = "info", skip_all)]
39 pub fn quotient_values(
40 &self,
41 constraints: &[&SymbolicExpressionDag<Val<SC>>],
42 extended_views: Vec<RapView<impl Matrix<Val<SC>>, Val<SC>, SC::Challenge>>,
43 quotient_degrees: &[u8],
44 ) -> QuotientData<SC> {
45 assert_eq!(constraints.len(), extended_views.len());
46 assert_eq!(constraints.len(), quotient_degrees.len());
47 let inner = izip!(constraints, extended_views, quotient_degrees)
48 .map(|(constraints, extended_view, "ient_degree)| {
49 self.single_rap_quotient_values(constraints, extended_view, quotient_degree)
50 })
51 .collect();
52 QuotientData { inner }
53 }
54
55 pub(super) fn single_rap_quotient_values(
56 &self,
57 constraints: &SymbolicExpressionDag<Val<SC>>,
58 view: RapView<impl Matrix<Val<SC>>, Val<SC>, SC::Challenge>,
59 quotient_degree: u8,
60 ) -> SingleQuotientData<SC> {
61 let log_trace_height = view.pair.log_trace_height;
62 let trace_domain = self
63 .pcs
64 .natural_domain_for_degree(1usize << log_trace_height);
65 let quotient_domain =
66 trace_domain.create_disjoint_domain(trace_domain.size() * quotient_degree as usize);
67
68 let (after_challenge_lde_on_quotient_domain, challenges, exposed_values_after_challenge): (
69 Vec<_>,
70 Vec<_>,
71 Vec<_>,
72 ) = multiunzip(view.per_phase.into_iter().map(|view| {
73 (
74 view.inner
75 .expect("gap in challenge phase not supported yet"),
76 view.challenges
77 .into_iter()
78 .map(PackedChallenge::<SC>::from_f)
79 .collect_vec(),
80 view.exposed_values
81 .into_iter()
82 .map(PackedChallenge::<SC>::from_f)
83 .collect_vec(),
84 )
85 }));
86
87 let quotient_values = compute_single_rap_quotient_values::<SC, _>(
88 constraints,
89 trace_domain,
90 quotient_domain,
91 view.pair.preprocessed,
92 view.pair.partitioned_main,
93 after_challenge_lde_on_quotient_domain,
94 &challenges,
95 self.alpha,
96 &view.pair.public_values,
97 &exposed_values_after_challenge,
98 );
99 SingleQuotientData {
100 quotient_degree: quotient_degree as usize,
101 quotient_domain,
102 quotient_values,
103 }
104 }
105
106 #[instrument(name = "commit to quotient poly chunks", skip_all)]
107 pub fn commit(&self, data: QuotientData<SC>) -> (Com<SC>, PcsData<SC>) {
108 let (log_trace_heights, quotient_domains_and_chunks): (Vec<_>, Vec<_>) = data
109 .split()
110 .into_iter()
111 .map(|q| {
112 (
113 log2_strict_usize(q.domain.size()) as u8,
114 (q.domain, q.chunk),
115 )
116 })
117 .unzip();
118 let (commit, data) = self.pcs.commit(quotient_domains_and_chunks);
119 (
120 commit,
121 PcsData {
122 data: Arc::new(data),
123 log_trace_heights,
124 },
125 )
126 }
127}
128
129pub(super) struct QuotientData<SC: StarkGenericConfig> {
131 inner: Vec<SingleQuotientData<SC>>,
132}
133
134impl<SC: StarkGenericConfig> QuotientData<SC> {
135 pub fn split(self) -> impl IntoIterator<Item = QuotientChunk<SC>> {
137 self.inner.into_iter().flat_map(|data| data.split())
138 }
139}
140
141pub(super) struct SingleQuotientData<SC: StarkGenericConfig> {
143 quotient_degree: usize,
144 quotient_domain: Domain<SC>,
146 quotient_values: Vec<SC::Challenge>,
148}
149
150impl<SC: StarkGenericConfig> SingleQuotientData<SC> {
151 pub fn split(self) -> impl IntoIterator<Item = QuotientChunk<SC>> {
156 let quotient_degree = self.quotient_degree;
157 let quotient_domain = self.quotient_domain;
158 let quotient_flat = RowMajorMatrix::new_col(self.quotient_values).flatten_to_base();
160 let quotient_chunks = quotient_domain.split_evals(quotient_degree, quotient_flat);
161 let qc_domains = quotient_domain.split_domains(quotient_degree);
162 qc_domains
163 .into_iter()
164 .zip_eq(quotient_chunks)
165 .map(|(domain, chunk)| QuotientChunk { domain, chunk })
166 }
167}
168
169pub struct QuotientChunk<SC: StarkGenericConfig> {
176 pub domain: Domain<SC>,
178 pub chunk: RowMajorMatrix<Val<SC>>,
181}