p3_binary_pcs/whir/
shape.rs1use p3_binary_field::TowerLevel;
4use p3_challenger::{FieldChallenger, GrindingChallenger};
5use p3_field::{ExtensionField, Field};
6use p3_sumcheck::ring_switch::bits::BitTensor;
7use p3_whir::WhirConfig;
8use p3_whir::transcript::query_draws;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct ProofShape {
15 pub stir_queries: usize,
17
18 pub opened_base_elements: usize,
20
21 pub opened_extension_elements: usize,
23
24 pub merkle_digests: usize,
28
29 pub sent_base_elements: usize,
31
32 pub sent_extension_elements: usize,
34
35 pub grinding_bits: usize,
37}
38
39impl ProofShape {
40 #[must_use]
44 pub fn of<EF, F, Challenger>(
45 config: &WhirConfig<EF, F, Challenger>,
46 opened_values: usize,
47 ) -> Self
48 where
49 F: Field,
50 EF: ExtensionField<F>,
51 Challenger: FieldChallenger<F> + GrindingChallenger<Witness = F>,
52 {
53 let num_rounds = config.round_parameters().len();
54 let mut shape = Self {
55 stir_queries: 0,
56 opened_base_elements: 0,
57 opened_extension_elements: 0,
58 merkle_digests: num_rounds,
60 sent_base_elements: config.num_variables() + num_rounds + 1,
62 sent_extension_elements: 2 * config.num_variables()
64 + (1 << config.final_sumcheck_rounds())
65 + config.commitment_ood_samples()
66 + opened_values,
67 grinding_bits: config.max_pow_bits(),
68 };
69
70 let rounds = config
71 .round_parameters()
72 .iter()
73 .map(|round| {
74 (
75 round.log_folded_domain_size,
76 round.folding_factor,
77 round.num_queries,
78 round.ood_samples,
79 )
80 })
81 .chain(core::iter::once({
82 let final_round = config.final_round_config();
83 (
84 final_round.log_folded_domain_size,
85 final_round.folding_factor,
86 config.terminal().num_queries,
87 0,
88 )
89 }));
90
91 for (index, (log_folded, folding, queries, ood)) in rounds.enumerate() {
92 let positions = if query_draws(1usize << log_folded, queries) == 0 {
94 1usize << log_folded
95 } else {
96 queries
97 };
98 let elements = positions << folding;
99
100 shape.stir_queries += positions;
101 if index == 0 {
102 shape.opened_base_elements += elements;
103 } else {
104 shape.opened_extension_elements += elements;
105 }
106 shape.merkle_digests += positions * log_folded;
107 shape.sent_extension_elements += ood;
108 }
109
110 shape
111 }
112
113 #[must_use]
120 pub fn of_bit_readings<EF, Challenger>(
121 config: &WhirConfig<EF, EF, Challenger>,
122 num_claims: usize,
123 successor_tensors: bool,
124 ) -> Self
125 where
126 EF: Field + TowerLevel,
127 Challenger: FieldChallenger<EF> + GrindingChallenger<Witness = EF>,
128 {
129 let surviving = num_claims.min(1);
131 let mut shape = Self::of(config, surviving);
132 let num_tensors = if successor_tensors { 3 } else { 1 };
134 let rows = num_tensors * BitTensor::<EF>::DIMENSION;
135 shape.sent_extension_elements +=
137 num_claims * rows + surviving * (2 * config.num_variables() + 1);
138 shape
139 }
140
141 #[must_use]
147 pub const fn max_bytes(
148 &self,
149 base_element_bytes: usize,
150 extension_element_bytes: usize,
151 digest_bytes: usize,
152 ) -> usize {
153 (self.opened_base_elements + self.sent_base_elements) * base_element_bytes
154 + (self.opened_extension_elements + self.sent_extension_elements)
155 * extension_element_bytes
156 + self.merkle_digests * digest_bytes
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::ProofShape;
163
164 const SHAPE: ProofShape = ProofShape {
166 stir_queries: 40,
167 opened_base_elements: 100,
168 opened_extension_elements: 200,
169 merkle_digests: 300,
170 sent_base_elements: 10,
171 sent_extension_elements: 50,
172 grinding_bits: 16,
173 };
174
175 #[test]
176 fn the_estimate_prices_each_part_at_its_own_width() {
177 assert_eq!(SHAPE.max_bytes(4, 16, 32), 14_040);
179 }
180
181 #[test]
182 fn a_wider_alphabet_raises_the_estimate_by_exactly_the_alphabet_run() {
183 assert_eq!(
185 SHAPE.max_bytes(16, 16, 32) - SHAPE.max_bytes(4, 16, 32),
186 110 * (16 - 4)
187 );
188 }
189}