1use crate::{
4 FancyArithmetic, FancyBinary,
5 fancy::{Fancy, FancyInput, FancyReveal, HasModulus},
6};
7use std::cmp::max;
8use swanky_channel::Channel;
9use swanky_error::ErrorKind;
10
11#[derive(Clone, Debug)]
17pub struct AnalyzerItem {
18 modulus: u16,
19 depth: usize,
20}
21
22impl HasModulus for AnalyzerItem {
23 fn modulus(&self) -> u16 {
24 self.modulus
25 }
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
36pub struct CircuitAnalyzer {
37 ninputs: usize,
38 nconstants: usize,
39 nands: usize,
40 nxors: usize,
41 nnegs: usize,
42 nadds: usize,
43 nsubs: usize,
44 ncmuls: usize,
45 nmuls: usize,
46 mul_depth: usize,
47}
48
49impl std::fmt::Display for CircuitAnalyzer {
50 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
51 writeln!(f, "computation info:")?;
52 writeln!(f, " number of inputs: {:16}", self.ninputs)?;
53 writeln!(f, " number of constants: {:16}", self.nconstants)?;
54 writeln!(f, " number of additions: {:16}", self.nadds)?;
55 writeln!(f, " number of subtractions: {:16}", self.nsubs)?;
56 writeln!(f, " number of cmuls: {:16}", self.ncmuls)?;
57 writeln!(f, " number of muls: {:16}", self.nmuls)?;
58 writeln!(f, " number of ands: {:16}", self.nands)?;
59 writeln!(f, " number of xors: {:16}", self.nxors)?;
60 writeln!(f, " number of negations: {:16}", self.nnegs)?;
61 writeln!(
62 f,
63 " total number of arithmetic gates(ADD, SUB, MUL, CMUL): {:16}",
64 self.nadds + self.nsubs + self.ncmuls + self.nmuls
65 )?;
66 writeln!(
67 f,
68 " total number of boolean gates (AND, XOR): {:16}",
69 self.nands + self.nxors
70 )?;
71 writeln!(f, " multiplicative depth: {:16}", self.mul_depth)?;
72 Ok(())
73 }
74}
75
76impl CircuitAnalyzer {
77 pub fn new() -> CircuitAnalyzer {
80 Default::default()
81 }
82 pub fn nands(&self) -> usize {
84 self.nands
85 }
86 pub fn ninputs(&self) -> usize {
88 self.ninputs
89 }
90}
91
92impl FancyInput for CircuitAnalyzer {
93 type Item = AnalyzerItem;
94
95 fn receive_many(
96 &mut self,
97 moduli: &[u16],
98 _: &mut Channel,
99 ) -> swanky_error::Result<Vec<Self::Item>> {
100 self.ninputs += moduli.len();
101 Ok(moduli
102 .iter()
103 .map(|q| AnalyzerItem {
104 modulus: *q,
105 depth: 0,
106 })
107 .collect())
108 }
109
110 fn encode_many(
111 &mut self,
112 _values: &[u16],
113 moduli: &[u16],
114 channel: &mut Channel,
115 ) -> swanky_error::Result<Vec<Self::Item>> {
116 self.receive_many(moduli, channel)
117 }
118}
119
120impl FancyBinary for CircuitAnalyzer {
121 fn xor(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item {
122 self.nxors += 1;
123 self.nadds += 1;
125 AnalyzerItem {
126 modulus: x.modulus,
127 depth: max(x.depth, y.depth),
129 }
130 }
131
132 fn and(
133 &mut self,
134 x: &Self::Item,
135 y: &Self::Item,
136 _: &mut Channel,
137 ) -> swanky_error::Result<Self::Item> {
138 self.nands += 1;
139 self.nmuls += 1;
141 Ok(AnalyzerItem {
142 modulus: x.modulus,
143 depth: max(x.depth, y.depth) + 1,
145 })
146 }
147 fn or(
148 &mut self,
149 x: &Self::Item,
150 y: &Self::Item,
151 _: &mut Channel,
152 ) -> swanky_error::Result<Self::Item> {
153 self.nands += 1;
154 self.nnegs += 3;
155 self.nmuls += 1;
157 Ok(AnalyzerItem {
158 modulus: x.modulus,
159 depth: max(x.depth, y.depth) + 1,
161 })
162 }
163 fn negate(&mut self, x: &Self::Item) -> Self::Item {
164 self.nnegs += 1;
165
166 self.nconstants += 1;
168 self.nxors += 1;
169
170 self.nadds += 1;
172 AnalyzerItem {
173 modulus: x.modulus,
174 depth: x.depth,
176 }
177 }
178}
179
180impl FancyArithmetic for CircuitAnalyzer {
181 fn add(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item {
182 self.nadds += 1;
183 AnalyzerItem {
184 modulus: x.modulus,
185 depth: max(x.depth, y.depth),
186 }
187 }
188
189 fn sub(&mut self, x: &Self::Item, y: &Self::Item) -> Self::Item {
190 self.nsubs += 1;
191 AnalyzerItem {
192 modulus: x.modulus,
193 depth: max(x.depth, y.depth),
194 }
195 }
196
197 fn cmul(&mut self, x: &Self::Item, _y: u16) -> Self::Item {
198 self.ncmuls += 1;
199 AnalyzerItem {
200 modulus: x.modulus,
201 depth: x.depth,
202 }
203 }
204
205 fn mul(
206 &mut self,
207 x: &Self::Item,
208 y: &Self::Item,
209 _: &mut Channel,
210 ) -> swanky_error::Result<Self::Item> {
211 self.nmuls += 1;
212 Ok(AnalyzerItem {
213 modulus: x.modulus,
214 depth: max(x.depth, y.depth) + 1,
215 })
216 }
217
218 fn proj(
219 &mut self,
220 _x: &Self::Item,
221 _q: u16,
222 _tt: Option<Vec<u16>>,
223 _: &mut Channel,
224 ) -> swanky_error::Result<Self::Item> {
225 swanky_error::bail!(
226 ErrorKind::UnsupportedError,
227 "Projection gates are unsupported"
228 )
229 }
230}
231
232impl Fancy for CircuitAnalyzer {
233 type Item = AnalyzerItem;
234
235 fn constant(&mut self, _val: u16, q: u16, _: &mut Channel) -> swanky_error::Result<Self::Item> {
236 self.nconstants += 1;
237 Ok(AnalyzerItem {
238 modulus: q,
239 depth: 0,
240 })
241 }
242
243 fn output(&mut self, x: &Self::Item, _: &mut Channel) -> swanky_error::Result<Option<u16>> {
244 self.mul_depth = max(self.mul_depth, x.depth);
245 Ok(None)
246 }
247}
248
249impl FancyReveal for CircuitAnalyzer {
250 fn reveal(&mut self, _x: &Self::Item, _: &mut Channel) -> swanky_error::Result<u16> {
251 Ok(0)
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258 use crate::BinaryGadgets;
259 #[test]
260 fn single_and_gate_count_is_correct() {
261 let nbits = 64;
262 let x = 0;
263 let y = 0;
264 Channel::with(std::io::empty(), |channel| {
265 let mut analyzer_test: CircuitAnalyzer = CircuitAnalyzer::new();
266 let x = analyzer_test.bin_encode(x, nbits, channel).unwrap();
267 let y = analyzer_test.bin_encode(y, nbits, channel).unwrap();
268 let _c = analyzer_test.bin_and(&x, &y, channel);
269
270 assert_eq!(analyzer_test.ninputs, 128);
271 assert_eq!(analyzer_test.nands, 64);
272 assert_eq!(analyzer_test.nxors, 0);
273 assert_eq!(analyzer_test.nconstants, 0);
274 assert_eq!(analyzer_test.nnegs, 0);
275 Ok(())
276 })
277 .unwrap();
278 }
279 #[test]
280 fn binary_addition_counts_are_correct() {
281 let nbits = 64;
282 let x = 0;
283 let y = 0;
284 Channel::with(std::io::empty(), |channel| {
285 let mut analyzer_test: CircuitAnalyzer = CircuitAnalyzer::new();
286 let x = analyzer_test.bin_encode(x, nbits, channel).unwrap();
287 let y = analyzer_test.bin_encode(y, nbits, channel).unwrap();
288 let _c = analyzer_test.bin_addition(&x, &y, channel);
290
291 assert_eq!(analyzer_test.ninputs, 128);
292 assert_eq!(analyzer_test.nands, 64);
293 assert_eq!(analyzer_test.nxors, 64 * 4 - 3);
298 assert_eq!(analyzer_test.nconstants, 0);
299 assert_eq!(analyzer_test.nnegs, 0);
300 Ok(())
301 })
302 .unwrap();
303 }
304 #[test]
305 fn binary_multiplication_counts_are_correct() {
306 let nbits = 64;
307 let x = 0;
308 let y = 0;
309 Channel::with(std::io::empty(), |channel| {
310 let mut analyzer_test: CircuitAnalyzer = CircuitAnalyzer::new();
311 let x = analyzer_test.bin_encode(x, nbits, channel).unwrap();
312 let y = analyzer_test.bin_encode(y, nbits, channel).unwrap();
313 let _c = analyzer_test.bin_mul(&x, &y, channel);
315
316 assert_eq!(analyzer_test.ninputs, 128);
322 assert_eq!(analyzer_test.nands, (65..128).sum::<usize>() + 64 * 64);
323 assert_eq!(
324 analyzer_test.nxors,
325 (65..128).sum::<usize>() * 4 - (3 * (128 - 65))
326 );
327 assert_eq!(analyzer_test.nconstants, 64);
328 assert_eq!(analyzer_test.nnegs, 0);
329 Ok(())
330 })
331 .unwrap();
332 }
333 #[test]
334 fn binary_twos_complement_counts_are_correct() {
335 let nbits = 64;
336 let x = 0;
337 Channel::with(std::io::empty(), |channel| {
338 let mut analyzer_test: CircuitAnalyzer = CircuitAnalyzer::new();
339 let x = analyzer_test.bin_encode(x, nbits, channel).unwrap();
340 let _c = analyzer_test.bin_twos_complement(&x, channel);
342
343 assert_eq!(analyzer_test.ninputs, 64);
344 assert_eq!(analyzer_test.nands, 63);
345 assert_eq!(analyzer_test.nxors, 63 * 4 - 3 + 64 + 2);
346 assert_eq!(analyzer_test.nconstants, 64 + 64);
347 assert_eq!(analyzer_test.nnegs, 64);
348 Ok(())
349 })
350 .unwrap();
351 }
352}