Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
graph.cpp
Go to the documentation of this file.
1#include "./graph.hpp"
5#include <algorithm>
6#include <array>
7#include <iomanip>
8#include <optional>
9#include <stack>
10
11using namespace bb::plookup;
12using namespace bb;
13
14namespace cdg {
15
23template <typename FF, typename CircuitBuilder>
25{
26 auto blocks_data = circuit_builder.blocks.get();
27 for (size_t i = 0; i < blocks_data.size(); i++) {
28 if (std::addressof(blocks_data[i]) == std::addressof(block)) {
29 return i;
30 }
31 }
32 return std::nullopt;
33}
34
49template <typename FF, typename CircuitBuilder>
50inline void StaticAnalyzer_<FF, CircuitBuilder>::process_gate_variables(std::vector<uint32_t>& gate_variables,
51 size_t gate_index,
52 size_t block_idx)
53{
54 auto unique_variables = std::unique(gate_variables.begin(), gate_variables.end());
55 gate_variables.erase(unique_variables, gate_variables.end());
56 if (gate_variables.empty()) {
57 return;
58 }
59 for (auto& var_idx : gate_variables) {
60 KeyPair key = std::make_pair(var_idx, block_idx);
61 variable_gates[key].emplace_back(gate_index);
62 }
63 for (const auto& variable_index : gate_variables) {
64 variables_gate_counts[variable_index] += 1;
65 }
66}
67
79template <typename FF, typename CircuitBuilder>
81 size_t index, size_t block_idx, auto& blk)
82{
83 auto q_arith = blk.q_arith()[index];
84 std::vector<uint32_t> all_variables;
85 std::vector<uint32_t> gate_variables;
86 std::vector<uint32_t> minigate_variables;
87 std::vector<std::vector<uint32_t>> all_gates_variables;
88 if (q_arith.is_zero()) {
89 return {};
90 }
91 auto q_m = blk.q_m()[index];
92 auto q_1 = blk.q_1()[index];
93 auto q_2 = blk.q_2()[index];
94 auto q_3 = blk.q_3()[index];
95 auto q_4 = blk.q_4()[index];
96
97 uint32_t left_idx = blk.w_l()[index];
98 uint32_t right_idx = blk.w_r()[index];
99 uint32_t out_idx = blk.w_o()[index];
100 uint32_t fourth_idx = blk.w_4()[index];
101 if (q_m.is_zero() && q_1 == 1 && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() && q_arith == FF::one()) {
102 // this is fixed_witness gate. So, variable index contains in left wire. So, we have to take only it.
103 fixed_variables.insert(this->to_real(left_idx));
104 } else if (!q_m.is_zero() || q_1 != FF::one() || !q_2.is_zero() || !q_3.is_zero() || !q_4.is_zero()) {
105 // this is not the gate for fix_witness, so we have to process this gate
106 if (!q_m.is_zero()) {
107 gate_variables.emplace_back(left_idx);
108 gate_variables.emplace_back(right_idx);
109 } else {
110 if (!q_1.is_zero()) {
111 gate_variables.emplace_back(left_idx);
112 }
113 if (!q_2.is_zero()) {
114 gate_variables.emplace_back(right_idx);
115 }
116 }
117
118 if (!q_3.is_zero()) {
119 gate_variables.emplace_back(out_idx);
120 }
121 if (!q_4.is_zero()) {
122 gate_variables.emplace_back(fourth_idx);
123 }
124 if (q_arith == FF(2)) {
125 // We have to use w_4_shift from the next gate
126 // if and only if the current gate isn't last, cause we can't
127 // look into the next gate
128 if (index != blk.size() - 1) {
129 gate_variables.emplace_back(blk.w_4()[index + 1]);
130 }
131 }
132 if (q_arith == FF(3)) {
133 // In this gate mini gate is enabled, we have 2 equations:
134 // q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0
135 // w_1 + w_4 - w_1_omega + q_m = 0
136 minigate_variables.emplace_back(left_idx);
137 minigate_variables.emplace_back(fourth_idx);
138 if (index != blk.size() - 1) {
139 gate_variables.emplace_back(blk.w_4()[index + 1]);
140 minigate_variables.emplace_back(blk.w_l()[index + 1]);
141 }
142 }
143 }
144 gate_variables = to_real(gate_variables);
145 minigate_variables = to_real(minigate_variables);
146 all_variables.reserve(gate_variables.size() + minigate_variables.size());
147 all_variables.insert(all_variables.end(), gate_variables.begin(), gate_variables.end());
148 all_variables.insert(all_variables.end(), minigate_variables.begin(), minigate_variables.end());
149 process_gate_variables(all_variables, index, block_idx);
150 return all_variables;
151}
152
164template <typename FF, typename CircuitBuilder>
166 size_t index, size_t block_idx, auto& blk)
167{
168 std::vector<uint32_t> gate_variables;
169 if (!blk.q_elliptic()[index].is_zero()) {
170 std::vector<uint32_t> first_row_variables;
171 std::vector<uint32_t> second_row_variables;
172 gate_variables.reserve(6);
173 bool is_elliptic_add_gate = !blk.q_1()[index].is_zero() && blk.q_m()[index].is_zero();
174 bool is_elliptic_dbl_gate = blk.q_1()[index].is_zero() && blk.q_m()[index] == FF::one();
175 first_row_variables.emplace_back(blk.w_r()[index]);
176 first_row_variables.emplace_back(blk.w_o()[index]);
177 if (index != blk.size() - 1) {
178 if (is_elliptic_add_gate) {
179 // if this gate is ecc_add_gate, we have to get indices x2, x3, y3, y2 from the next gate
180 second_row_variables.emplace_back(blk.w_l()[index + 1]);
181 second_row_variables.emplace_back(blk.w_r()[index + 1]);
182 second_row_variables.emplace_back(blk.w_o()[index + 1]);
183 second_row_variables.emplace_back(blk.w_4()[index + 1]);
184 }
185 if (is_elliptic_dbl_gate) {
186 // if this gate is ecc_dbl_gate, we have to indices x3, y3 from right and output wires
187 second_row_variables.emplace_back(blk.w_r()[index + 1]);
188 second_row_variables.emplace_back(blk.w_o()[index + 1]);
189 }
190 }
191 if (!first_row_variables.empty()) {
192 first_row_variables = to_real(first_row_variables);
193 process_gate_variables(first_row_variables, index, block_idx);
194 gate_variables.insert(gate_variables.end(), first_row_variables.cbegin(), first_row_variables.cend());
195 }
196 if (!second_row_variables.empty()) {
197 second_row_variables = to_real(second_row_variables);
198 process_gate_variables(second_row_variables, index, block_idx);
199 gate_variables.insert(gate_variables.end(), second_row_variables.cbegin(), second_row_variables.cend());
200 }
201 }
202 return gate_variables;
203}
204
216template <typename FF, typename CircuitBuilder>
218 size_t index, size_t blk_idx, auto& block)
219{
220 std::vector<uint32_t> gate_variables = {};
221 if (!block.q_delta_range()[index].is_zero()) {
222 std::vector<uint32_t> row_variables = {
223 block.w_l()[index], block.w_r()[index], block.w_o()[index], block.w_4()[index]
224 };
225 /*
226 sometimes process_range_list function adds variables with zero_idx in beginning of vector with indices
227 in order to pad a size of indices to gate width. But tool has to ignore these additional variables
228 */
229 for (const auto& var_idx : row_variables) {
230 if (var_idx != circuit_builder.zero_idx()) {
231 gate_variables.emplace_back(var_idx);
232 }
233 }
234 if (index != block.size() - 1 && block.w_l()[index + 1] != circuit_builder.zero_idx()) {
235 gate_variables.emplace_back(block.w_l()[index + 1]);
236 }
237 }
238 gate_variables = to_real(gate_variables);
239 process_gate_variables(gate_variables, index, blk_idx);
240 return gate_variables;
241}
242
254template <typename FF, typename CircuitBuilder>
256 size_t blk_idx,
257 auto& block)
258{
259 std::vector<uint32_t> gate_variables;
260 auto q_lookup = block.q_lookup()[index];
261 if (!q_lookup.is_zero()) {
262 gate_variables.reserve(6);
263 auto q_2 = block.q_2()[index];
264 auto q_m = block.q_m()[index];
265 auto q_c = block.q_c()[index];
266 gate_variables.emplace_back(block.w_l()[index]);
267 gate_variables.emplace_back(block.w_r()[index]);
268 gate_variables.emplace_back(block.w_o()[index]);
269 if (index < block.size() - 1) {
270 if (!q_2.is_zero()) {
271 gate_variables.emplace_back(block.w_l()[index + 1]);
272 }
273 if (!q_m.is_zero()) {
274 gate_variables.emplace_back(block.w_r()[index + 1]);
275 }
276 if (!q_c.is_zero()) {
277 gate_variables.emplace_back(block.w_o()[index + 1]);
278 }
279 }
280 gate_variables = to_real(gate_variables);
281 process_gate_variables(gate_variables, index, blk_idx);
282 }
283 return gate_variables;
284}
285
295template <typename FF, typename CircuitBuilder>
297 size_t blk_idx,
298 auto& block)
299{
300 std::vector<uint32_t> gate_variables;
301 auto internal_selector = block.q_poseidon2_internal()[index];
302 auto external_selector = block.q_poseidon2_external()[index];
303 if (!internal_selector.is_zero() || !external_selector.is_zero()) {
304 gate_variables.reserve(8);
305 gate_variables.emplace_back(block.w_l()[index]);
306 gate_variables.emplace_back(block.w_r()[index]);
307 gate_variables.emplace_back(block.w_o()[index]);
308 gate_variables.emplace_back(block.w_4()[index]);
309 if (index != block.size() - 1) {
310 gate_variables.emplace_back(block.w_l()[index + 1]);
311 gate_variables.emplace_back(block.w_r()[index + 1]);
312 gate_variables.emplace_back(block.w_o()[index + 1]);
313 gate_variables.emplace_back(block.w_4()[index + 1]);
314 }
315 gate_variables = to_real(gate_variables);
316 process_gate_variables(gate_variables, index, blk_idx);
317 }
318 return gate_variables;
319}
320
330template <typename FF, typename CircuitBuilder>
332 size_t blk_idx,
333 auto& block)
334{
335 std::vector<uint32_t> gate_variables;
336 if (!block.q_memory()[index].is_zero()) {
337 gate_variables.reserve(8);
338 auto q_1 = block.q_1()[index];
339 auto q_2 = block.q_2()[index];
340 auto q_3 = block.q_3()[index];
341 auto q_4 = block.q_4()[index];
342 if (q_1 == FF::one() && q_4 == FF::one()) {
343 BB_ASSERT(q_3.is_zero());
344 // ram timestamp check
345 if (index < block.size() - 1) {
346 gate_variables.insert(gate_variables.end(),
347 { block.w_r()[index + 1],
348 block.w_r()[index],
349 block.w_l()[index],
350 block.w_l()[index + 1],
351 block.w_o()[index] });
352 }
353 } else if (q_1 == FF::one() && q_2 == FF::one()) {
354 BB_ASSERT(q_3.is_zero());
355 // rom constitency check
356 if (index < block.size() - 1) {
357 gate_variables.insert(
358 gate_variables.end(),
359 { block.w_l()[index], block.w_l()[index + 1], block.w_4()[index], block.w_4()[index + 1] });
360 }
361 } else {
362 // ram constitency check
363 if (!q_3.is_zero()) {
364 if (index < block.size() - 1) {
365 gate_variables.insert(gate_variables.end(),
366 { block.w_o()[index],
367 block.w_4()[index],
368 block.w_l()[index + 1],
369 block.w_r()[index + 1],
370 block.w_o()[index + 1],
371 block.w_4()[index + 1] });
372 }
373 }
374 }
375 }
376 gate_variables = to_real(gate_variables);
377 process_gate_variables(gate_variables, index, blk_idx);
378 return gate_variables;
379}
380
390template <typename FF, typename CircuitBuilder>
392 size_t index, size_t blk_idx, auto& block)
393{
394 std::vector<uint32_t> gate_variables;
395 if (!block.q_nnf()[index].is_zero()) {
396 gate_variables.reserve(8);
397 [[maybe_unused]] auto q_1 = block.q_1()[index];
398 auto q_2 = block.q_2()[index];
399 auto q_3 = block.q_3()[index];
400 auto q_4 = block.q_4()[index];
401 auto q_m = block.q_m()[index];
402
403 auto w_l = block.w_l()[index];
404 auto w_r = block.w_r()[index];
405 auto w_o = block.w_o()[index];
406 auto w_4 = block.w_4()[index];
407 if (q_3 == FF::one() && q_4 == FF::one()) {
408 // bigfield limb accumulation 1
409 if (index < block.size() - 1) {
410 gate_variables.insert(gate_variables.end(),
411 { w_l, w_r, w_o, w_4, block.w_l()[index + 1], block.w_r()[index + 1] }); // 6
412 }
413 } else if (q_3 == FF::one() && q_m == FF::one()) {
414 // bigfield limb accumulation 2
415 if (index < block.size() - 1) {
416 gate_variables.insert(gate_variables.end(),
417 { w_o,
418 w_4,
419 block.w_l()[index + 1],
420 block.w_r()[index + 1],
421 block.w_o()[index + 1],
422 block.w_4()[index + 1] });
423 }
424 } else if (q_2 == FF::one() && (q_3 == FF::one() || q_4 == FF::one() || q_m == FF::one())) {
425 // bigfield product cases
426 if (index < block.size() - 1) {
427 std::vector<uint32_t> limb_subproduct_vars = {
428 w_l, w_r, block.w_l()[index + 1], block.w_r()[index + 1]
429 };
430 if (q_3 == FF::one()) {
431 // bigfield product 1
432 BB_ASSERT(q_4.is_zero() && q_m.is_zero());
433 gate_variables.insert(
434 gate_variables.end(), limb_subproduct_vars.begin(), limb_subproduct_vars.end());
435 gate_variables.insert(gate_variables.end(), { w_o, w_4 });
436 }
437 if (q_4 == FF::one()) {
438 // bigfield product 2
439 BB_ASSERT(q_3.is_zero() && q_m.is_zero());
440 std::vector<uint32_t> non_native_field_gate_2 = { w_l, w_4, w_r, w_o, block.w_o()[index + 1] };
441 gate_variables.insert(
442 gate_variables.end(), non_native_field_gate_2.begin(), non_native_field_gate_2.end());
443 gate_variables.emplace_back(block.w_4()[index + 1]);
444 gate_variables.insert(
445 gate_variables.end(), limb_subproduct_vars.begin(), limb_subproduct_vars.end());
446 }
447 if (q_m == FF::one()) {
448 // bigfield product 3
449 BB_ASSERT(q_4.is_zero() && q_3.is_zero());
450 gate_variables.insert(
451 gate_variables.end(), limb_subproduct_vars.begin(), limb_subproduct_vars.end());
452 gate_variables.insert(gate_variables.end(),
453 { w_4, block.w_o()[index + 1], block.w_4()[index + 1] });
454 }
455 }
456 }
457 }
458 gate_variables = to_real(gate_variables);
459 process_gate_variables(gate_variables, index, blk_idx);
460 return gate_variables;
461}
462
470template <typename FF, typename CircuitBuilder>
472 const bb::RomTranscript& rom_array)
473{
474 // Every RomTranscript data structure has 2 main components that are interested for static analyzer:
475 // 1) records contains values that were put in the gate, we can use them to create connections between variables
476 // 2) states contains values witness indexes that we can find in the ROM record in the RomTrascript, so we can
477 // ignore state of the ROM transcript, because we still can connect all variables using variables from records.
478 std::vector<uint32_t> rom_table_variables;
479 if (std::optional<size_t> blk_idx = find_block_index(circuit_builder.blocks.memory); blk_idx) {
480 // Every RomTranscript data structure has 2 main components that are interested for static analyzer:
481 // 1) records contains values that were put in the gate, we can use them to create connections between variables
482 // 2) states contains values witness indexes that we can find in the ROM record in the RomTrascript, so we can
483 // ignore state of the ROM transcript, because we still can connect all variables using variables from records.
484 for (const auto& record : rom_array.records) {
485 std::vector<uint32_t> gate_variables;
486 size_t gate_index = record.gate_index;
487
488 auto q_1 = circuit_builder.blocks.memory.q_1()[gate_index];
489 auto q_2 = circuit_builder.blocks.memory.q_2()[gate_index];
490 auto q_3 = circuit_builder.blocks.memory.q_3()[gate_index];
491 auto q_4 = circuit_builder.blocks.memory.q_4()[gate_index];
492 auto q_m = circuit_builder.blocks.memory.q_m()[gate_index];
493 auto q_c = circuit_builder.blocks.memory.q_c()[gate_index];
494
495 auto index_witness = record.index_witness;
496 auto vc1_witness = record.value_column1_witness; // state[0] from RomTranscript
497 auto vc2_witness = record.value_column2_witness; // state[1] from RomTranscript
498 auto record_witness = record.record_witness;
499
500 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() &&
501 q_c.is_zero()) {
502 // By default ROM read gate uses variables (w_1, w_2, w_3, w_4) = (index_witness, vc1_witness,
503 // vc2_witness, record_witness) So we can update all of them
504 gate_variables.emplace_back(index_witness);
505 if (vc1_witness != circuit_builder.zero_idx()) {
506 gate_variables.emplace_back(vc1_witness);
507 }
508 if (vc2_witness != circuit_builder.zero_idx()) {
509 gate_variables.emplace_back(vc2_witness);
510 }
511 gate_variables.emplace_back(record_witness);
512 }
513 gate_variables = to_real(gate_variables);
514 process_gate_variables(gate_variables, gate_index, *blk_idx);
515 // after process_gate_variables function gate_variables constists of real variables indexes, so we can
516 // add all this variables in the final vector to connect all of them
517 if (!gate_variables.empty()) {
518 rom_table_variables.insert(rom_table_variables.end(), gate_variables.begin(), gate_variables.end());
519 }
520 }
521 }
522 return rom_table_variables;
523}
524
533template <typename FF, typename CircuitBuilder>
535 const bb::RamTranscript& ram_array)
536{
537 std::vector<uint32_t> ram_table_variables;
538 if (std::optional<size_t> blk_idx = find_block_index(circuit_builder.blocks.memory); blk_idx) {
539 for (const auto& record : ram_array.records) {
540 std::vector<uint32_t> gate_variables;
541 size_t gate_index = record.gate_index;
542
543 auto q_1 = circuit_builder.blocks.memory.q_1()[gate_index];
544 auto q_2 = circuit_builder.blocks.memory.q_2()[gate_index];
545 auto q_3 = circuit_builder.blocks.memory.q_3()[gate_index];
546 auto q_4 = circuit_builder.blocks.memory.q_4()[gate_index];
547 auto q_m = circuit_builder.blocks.memory.q_m()[gate_index];
548 auto q_c = circuit_builder.blocks.memory.q_c()[gate_index];
549
550 auto index_witness = record.index_witness;
551 auto timestamp_witness = record.timestamp_witness;
552 auto value_witness = record.value_witness;
553 auto record_witness = record.record_witness;
554
555 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() &&
556 (q_c.is_zero() || q_c == FF::one())) {
557 // By default RAM read/write gate uses variables (w_1, w_2, w_3, w_4) = (index_witness,
558 // timestamp_witness, value_witness, record_witness) So we can update all of them
559 gate_variables.emplace_back(index_witness);
560 if (timestamp_witness != circuit_builder.zero_idx()) {
561 gate_variables.emplace_back(timestamp_witness);
562 }
563 if (value_witness != circuit_builder.zero_idx()) {
564 gate_variables.emplace_back(value_witness);
565 }
566 gate_variables.emplace_back(record_witness);
567 }
568 gate_variables = to_real(gate_variables);
569 process_gate_variables(gate_variables, gate_index, *blk_idx);
570 // after process_gate_variables function gate_variables constists of real variables indexes, so we can add
571 // all these variables in the final vector to connect all of them
572 ram_table_variables.insert(ram_table_variables.end(), gate_variables.begin(), gate_variables.end());
573 }
574 }
575 return ram_table_variables;
576}
577
588template <typename FF, typename CircuitBuilder>
590 size_t block_idx,
591 auto& blk)
592{
593 std::vector<uint32_t> gate_variables;
594 if (!blk.q_busread()[index].is_zero()) {
595 gate_variables.insert(gate_variables.end(), { blk.w_l()[index], blk.w_r()[index] });
596 gate_variables = to_real(gate_variables);
597 process_gate_variables(gate_variables, index, block_idx);
598 }
599 return gate_variables;
600}
601
613template <typename FF, typename CircuitBuilder>
615 size_t block_idx,
616 auto& blk)
617{
618 std::vector<uint32_t> gate_variables;
619 std::vector<uint32_t> first_row_variables;
620 std::vector<uint32_t> second_row_variables;
621 auto w1 = blk.w_l()[index]; // get opcode of operation, because function get_ecc_op_idx returns type
622 // uint32_t and it adds as w1
623 if (w1 != circuit_builder.zero_idx()) {
624 // this is opcode and start of the UltraOp element
625 first_row_variables.insert(
626 first_row_variables.end(),
627 { w1, blk.w_r()[index], blk.w_o()[index], blk.w_4()[index] }); // add op, x_lo, x_hi, y_lo
628 if (index < blk.size() - 1) {
629 second_row_variables.insert(
630 second_row_variables.end(),
631 { blk.w_r()[index + 1], blk.w_o()[index + 1], blk.w_4()[index + 1] }); // add y_hi, z1, z2
632 }
633 first_row_variables = to_real(first_row_variables);
634 second_row_variables = to_real(second_row_variables);
635 process_gate_variables(first_row_variables, index, block_idx);
636 process_gate_variables(second_row_variables, index, block_idx);
637 }
638 if (!first_row_variables.empty()) {
639 gate_variables.insert(gate_variables.end(), first_row_variables.cbegin(), first_row_variables.cend());
640 }
641 if (!second_row_variables.empty()) {
642 gate_variables.insert(gate_variables.end(), second_row_variables.cbegin(), second_row_variables.cend());
643 }
644 return gate_variables;
645}
646
647template <typename FF, typename CircuitBuilder> void StaticAnalyzer_<FF, CircuitBuilder>::process_execution_trace()
648{
649 auto block_data = circuit_builder.blocks.get();
650
651 // We have to determine pub_inputs block index based on circuit builder type, because we have to skip it.
652 // If type of CircuitBuilder is UltraCircuitBuilder, the pub_inputs block is the first block so we can set
653 // pub_inputs_block_idx
654 size_t pub_inputs_block_idx = 0;
655
656 // For MegaCircuitBuilder, pub_inputs block has index 3
657 if constexpr (IsMegaBuilder<CircuitBuilder>) {
658 pub_inputs_block_idx = 3;
659 }
660
661 for (size_t blk_idx = 0; blk_idx < block_data.size(); blk_idx++) {
662 if (block_data[blk_idx].size() == 0 || blk_idx == pub_inputs_block_idx) {
663 continue;
664 }
665 std::vector<uint32_t> sorted_variables;
666 std::vector<uint32_t> eccop_variables;
667 for (size_t gate_idx = 0; gate_idx < block_data[blk_idx].size(); gate_idx++) {
669 get_arithmetic_gate_connected_component(gate_idx, blk_idx, block_data[blk_idx]),
670 get_elliptic_gate_connected_component(gate_idx, blk_idx, block_data[blk_idx]),
671 get_plookup_gate_connected_component(gate_idx, blk_idx, block_data[blk_idx]),
672 get_poseido2s_gate_connected_component(gate_idx, blk_idx, block_data[blk_idx]),
673 get_non_native_field_gate_connected_component(gate_idx, blk_idx, block_data[blk_idx]),
674 get_memory_gate_connected_component(gate_idx, blk_idx, block_data[blk_idx]),
675 get_sort_constraint_connected_component(gate_idx, blk_idx, block_data[blk_idx])
676 };
677 auto non_empty_count =
678 std::count_if(all_cc.begin(), all_cc.end(), [](const auto& vec) { return !vec.empty(); });
679 BB_ASSERT(non_empty_count < 2U);
680 auto not_empty_cc_it =
681 std::find_if(all_cc.begin(), all_cc.end(), [](const auto& vec) { return !vec.empty(); });
682 if (not_empty_cc_it != all_cc.end() && connect_variables) {
683 connect_all_variables_in_vector(*not_empty_cc_it);
684 }
685 if constexpr (IsMegaBuilder<CircuitBuilder>) {
686 // If type of CircuitBuilder is MegaCircuitBuilder, we'll try to process blocks like they can be
687 // databus or eccop
688 auto databus_variables = get_databus_connected_component(gate_idx, blk_idx, block_data[blk_idx]);
689 if (connect_variables) {
690 connect_all_variables_in_vector(databus_variables);
691 }
692 auto eccop_gate_variables = get_eccop_part_connected_component(gate_idx, blk_idx, block_data[blk_idx]);
693 if (connect_variables) {
694 if (!eccop_gate_variables.empty()) {
695 // The gotten vector of variables contains all variables from UltraOp element of the table
696 eccop_variables.insert(
697 eccop_variables.end(), eccop_gate_variables.begin(), eccop_gate_variables.end());
698 // if a current opcode is responsible for equality and reset, we have to connect all
699 // variables in global vector and clear it for the next parts
700 if (eccop_gate_variables[0] == circuit_builder.equality_op_idx) {
701 connect_all_variables_in_vector(eccop_variables);
702 eccop_variables.clear();
703 }
704 }
705 }
706 }
707 }
708 }
709
710 const auto& rom_arrays = circuit_builder.rom_ram_logic.rom_arrays;
711 if (!rom_arrays.empty()) {
712 for (const auto& rom_array : rom_arrays) {
713 std::vector<uint32_t> variable_indices = get_rom_table_connected_component(rom_array);
714 if (connect_variables) {
715 connect_all_variables_in_vector(variable_indices);
716 }
717 }
718 }
719
720 const auto& ram_arrays = circuit_builder.rom_ram_logic.ram_arrays;
721 if (!ram_arrays.empty()) {
722 for (const auto& ram_array : ram_arrays) {
723 std::vector<uint32_t> variable_indices = get_ram_table_connected_component(ram_array);
724 if (connect_variables) {
725 connect_all_variables_in_vector(variable_indices);
726 }
727 }
728 }
729}
730
753template <typename FF, typename CircuitBuilder>
755 : circuit_builder(circuit_builder)
756 , connect_variables(connect_variables)
757{
758 variables_gate_counts = std::unordered_map<uint32_t, size_t>(circuit_builder.real_variable_index.size());
761 variables_degree = std::unordered_map<uint32_t, size_t>(circuit_builder.real_variable_index.size());
762 for (const auto& variable_index : circuit_builder.real_variable_index) {
763 variables_gate_counts[variable_index] = 0;
764 variables_degree[variable_index] = 0;
765 variable_adjacency_lists[variable_index] = {};
766 }
769}
770
779template <typename FF, typename CircuitBuilder>
781{
782 constant_variable_indices_set.clear();
783 const auto& constant_variable_indices = circuit_builder.constant_variable_indices;
784 for (const auto& pair : constant_variable_indices) {
785 constant_variable_indices_set.insert(pair.second);
786 }
787}
788
796template <typename FF, typename CircuitBuilder>
798{
799 uint32_t real_variable_index = circuit_builder.real_variable_index[variable_index];
800 return constant_variable_indices_set.find(real_variable_index) == constant_variable_indices_set.end();
801}
802
813template <typename FF, typename CircuitBuilder>
814void StaticAnalyzer_<FF, CircuitBuilder>::connect_all_variables_in_vector(const std::vector<uint32_t>& variables_vector)
815{
816 if (variables_vector.empty()) {
817 return;
818 }
819 std::vector<uint32_t> filtered_variables_vector;
820 filtered_variables_vector.reserve(variables_vector.size());
821 // Only copy non-zero and non-constant variables
822 std::copy_if(variables_vector.begin(),
823 variables_vector.end(),
824 std::back_inserter(filtered_variables_vector),
825 [&](uint32_t variable_index) {
826 return variable_index != circuit_builder.zero_idx() &&
827 this->check_is_not_constant_variable(variable_index);
828 });
829 // Remove duplicates
830 auto unique_pointer = std::unique(filtered_variables_vector.begin(), filtered_variables_vector.end());
831 filtered_variables_vector.erase(unique_pointer, filtered_variables_vector.end());
832 if (filtered_variables_vector.size() < 2) {
833 return;
834 }
835 for (size_t i = 0; i < filtered_variables_vector.size() - 1; i++) {
836 add_new_edge(filtered_variables_vector[i], filtered_variables_vector[i + 1]);
837 }
838}
839
848template <typename FF, typename CircuitBuilder>
849void StaticAnalyzer_<FF, CircuitBuilder>::add_new_edge(const uint32_t& first_variable_index,
850 const uint32_t& second_variable_index)
851{
852 variable_adjacency_lists[first_variable_index].emplace_back(second_variable_index);
853 variable_adjacency_lists[second_variable_index].emplace_back(first_variable_index);
854 variables_degree[first_variable_index] += 1;
855 variables_degree[second_variable_index] += 1;
856}
857
867template <typename FF, typename CircuitBuilder>
869 std::unordered_set<uint32_t>& is_used,
870 std::vector<uint32_t>& connected_component)
871{
872 std::stack<uint32_t> variable_stack;
873 variable_stack.push(variable_index);
874 while (!variable_stack.empty()) {
875 uint32_t current_index = variable_stack.top();
876 variable_stack.pop();
877 if (!is_used.contains(current_index)) {
878 is_used.insert(current_index);
879 connected_component.emplace_back(current_index);
880 for (const auto& it : variable_adjacency_lists[current_index]) {
881 variable_stack.push(it);
882 }
883 }
884 }
885}
886
896template <typename FF, typename CircuitBuilder>
898{
899 if (!connect_variables) {
900 throw std::runtime_error("find_connected_components() can only be called when connect_variables is true");
901 }
902 connected_components.clear();
903 std::unordered_set<uint32_t> visited;
904 for (const auto& pair : variable_adjacency_lists) {
905 if (pair.first != 0 && variables_degree[pair.first] > 0) {
906 if (!visited.contains(pair.first)) {
907 std::vector<uint32_t> variable_indices;
908 depth_first_search(pair.first, visited, variable_indices);
909 std::sort(variable_indices.begin(), variable_indices.end());
910 connected_components.emplace_back(ConnectedComponent(variable_indices));
911 }
912 }
913 }
914 mark_range_list_connected_components();
915 mark_finalize_connected_components();
916 mark_process_rom_connected_component();
917 return connected_components;
918}
919
928template <typename FF, typename CircuitBuilder>
929bool StaticAnalyzer_<FF, CircuitBuilder>::is_gate_sorted_rom(size_t memory_block_idx, size_t gate_idx) const
930{
931
932 auto& memory_block = circuit_builder.blocks.get()[memory_block_idx];
933 return memory_block.q_memory()[gate_idx] == FF::one() && memory_block.q_1()[gate_idx] == FF::one() &&
934 memory_block.q_2()[gate_idx] == FF::one();
935}
936
945template <typename FF, typename CircuitBuilder>
947{
948 bool result = false;
949 KeyPair key = { var_idx, blk_idx };
950 auto it = variable_gates.find(key);
951 if (it != variable_gates.end()) {
952 const auto& gates = it->second;
953 result = std::all_of(gates.begin(), gates.end(), [this, blk_idx](size_t gate_idx) {
954 return is_gate_sorted_rom(blk_idx, gate_idx);
955 });
956 }
957 return result;
958}
959
969template <typename FF, typename CircuitBuilder>
971{
972 std::optional<size_t> block_idx_opt = find_block_index(circuit_builder.blocks.memory);
973 if (!block_idx_opt.has_value()) {
974 return;
975 }
976 size_t block_idx = block_idx_opt.value();
977 for (auto& cc : connected_components) {
978 const std::vector<uint32_t>& variables = cc.vars();
979 cc.is_process_rom_cc =
980 std::all_of(variables.begin(), variables.end(), [this, block_idx](uint32_t real_var_idx) {
981 return variable_only_in_sorted_rom_gates(real_var_idx, block_idx);
982 });
983 }
984}
985
995template <typename FF, typename CircuitBuilder>
997{
998 const auto& tags = circuit_builder.real_variable_tags;
999 std::unordered_set<uint32_t> tau_tags;
1000 for (const auto& pair : circuit_builder.range_lists) {
1001 tau_tags.insert(pair.second.tau_tag);
1002 }
1003 for (auto& cc : connected_components) {
1004 const auto& variables = cc.variable_indices;
1005 const uint32_t first_tag = tags[variables[0]];
1006 if (tau_tags.contains(first_tag)) {
1007 cc.is_range_list_cc =
1008 std::all_of(variables.begin() + 1, variables.end(), [&tags, first_tag](uint32_t var_idx) {
1009 return tags[var_idx] == first_tag;
1010 });
1011 }
1012 }
1013}
1014
1023template <typename FF, typename CircuitBuilder>
1025{
1026 const auto& finalize_witnesses = circuit_builder.get_finalize_witnesses();
1027 for (auto& cc : connected_components) {
1028 const auto& vars = cc.vars();
1029 cc.is_finalize_cc = std::all_of(vars.begin(), vars.end(), [&finalize_witnesses](uint32_t var_idx) {
1030 return finalize_witnesses.contains(var_idx);
1031 });
1032 }
1033}
1034
1051template <typename FF, typename CircuitBuilder>
1053{
1054 auto& arithmetic_block = circuit_builder.blocks.arithmetic;
1055 auto zero_idx = circuit_builder.zero_idx();
1056 size_t current_index = index;
1057 std::vector<uint32_t> accumulators_indices;
1058 while (true) {
1059 // we have to remove left, right and output wires of the current gate, cause they'are new_limbs, and they
1060 // are useless for the analyzer
1061 auto fourth_idx = arithmetic_block.w_4()[current_index];
1062 accumulators_indices.emplace_back(this->to_real(fourth_idx));
1063 auto left_idx = arithmetic_block.w_l()[current_index];
1064 if (left_idx != zero_idx) {
1065 variables_in_one_gate.erase(this->to_real(left_idx));
1066 }
1067 auto right_idx = arithmetic_block.w_r()[current_index];
1068 if (right_idx != zero_idx) {
1069 variables_in_one_gate.erase(this->to_real(right_idx));
1070 }
1071 auto out_idx = arithmetic_block.w_o()[current_index];
1072 if (out_idx != zero_idx) {
1073 variables_in_one_gate.erase(this->to_real(out_idx));
1074 }
1075 auto q_arith = arithmetic_block.q_arith()[current_index];
1076 if (q_arith == 1 || current_index == arithmetic_block.size() - 1) {
1077 // this is the last gate in this chain, or we can't go next, so we have to stop a loop
1078 break;
1079 }
1080 current_index++;
1081 }
1082 for (size_t i = 0; i < accumulators_indices.size(); i++) {
1083 if (i == 0) {
1084 // the first variable in accumulators is the variable which decompose was created. So, we have to
1085 // decrement variable_gate_counts for this variable
1086 variables_gate_counts[accumulators_indices[i]] -= 1;
1087 } else {
1088 // next accumulators are useless variables that are not interested for the analyzer. So, for these
1089 // variables we can nullify variables_gate_counts
1090 variables_gate_counts[accumulators_indices[i]] = 0;
1091 }
1092 }
1093 // we don't want to make variables_gate_counts for intermediate variables negative, so, can go to the next gates
1094 return current_index;
1095}
1096
1104template <typename FF, typename CircuitBuilder>
1106 const std::unordered_set<uint32_t>& decompose_variables)
1107{
1108 auto is_power_two = [&](const uint256_t& number) { return number > 0 && ((number & (number - 1)) == 0); };
1109 auto find_position = [&](uint32_t variable_index) {
1110 return decompose_variables.contains(this->to_real(variable_index));
1111 };
1112 auto& arithmetic_block = circuit_builder.blocks.arithmetic;
1113 if (arithmetic_block.size() > 0) {
1114 for (size_t i = 0; i < arithmetic_block.size(); i++) {
1115 auto q_1 = arithmetic_block.q_1()[i];
1116 auto q_2 = arithmetic_block.q_2()[i];
1117 auto q_3 = arithmetic_block.q_3()[i];
1118 // big addition gate from decompose has selectors, which have the next property:
1119 // q_1 = (1) << shifts[0], target_range_bitnum * (3 * i),
1120 // q_2 = (1) << shifts[1], target_range_bitnum * (3 * i + 1),
1121 // q_3 = (1) << shifts[2], target_range_bitnum * (3 * i + 2)
1122 // so, they are power of two and satisfying the following equality: q_2 * q_2 = q_1 * q_3
1123 // this way we can differ them from other arithmetic gates
1124 bool q_1_is_power_two = is_power_two(q_1);
1125 bool q_2_is_power_two = is_power_two(q_2);
1126 bool q_3_is_power_two = is_power_two(q_3);
1127 if (q_2 * q_2 == q_1 * q_3 && q_1_is_power_two && q_2_is_power_two && q_3_is_power_two) {
1128 uint32_t left_idx = arithmetic_block.w_l()[i];
1129 uint32_t right_idx = arithmetic_block.w_r()[i];
1130 uint32_t out_idx = arithmetic_block.w_o()[i];
1131 uint32_t fourth_idx = arithmetic_block.w_4()[i];
1132 bool find_left = find_position(left_idx);
1133 bool find_right = find_position(right_idx);
1134 bool find_out = find_position(out_idx);
1135 bool find_fourth = find_position(fourth_idx);
1136 if (((find_left && find_right && find_out) || (find_left && find_right && !find_out) ||
1137 (find_left && find_right && !find_out) || (find_left && !find_right && !find_out)) &&
1138 !find_fourth) {
1139 i = this->process_current_decompose_chain(i);
1140 }
1141 }
1142 }
1143 }
1144}
1145
1154template <typename FF, typename CircuitBuilder>
1156{
1157 const auto& range_lists = circuit_builder.range_lists;
1158 std::unordered_set<uint32_t> range_lists_tau_tags;
1159 std::unordered_set<uint32_t> range_lists_range_tags;
1160 const auto& real_variable_tags = circuit_builder.real_variable_tags;
1161 for (const auto& pair : range_lists) {
1162 typename CircuitBuilder::RangeList list = pair.second;
1163 range_lists_tau_tags.insert(list.tau_tag);
1164 range_lists_range_tags.insert(list.range_tag);
1165 }
1166 for (uint32_t real_index = 0; real_index < real_variable_tags.size(); real_index++) {
1167 if (variables_in_one_gate.contains(real_index)) {
1168 // this if helps us to remove variables from delta_range_constraints when finalize_circuit() function
1169 // was called
1170 if (range_lists_tau_tags.contains(real_variable_tags[real_index])) {
1171 variables_in_one_gate.erase(real_index);
1172 }
1173 // this if helps us to remove variables from range_constraints when range_constraint_into_two_limbs
1174 // function was called
1175 if (range_lists_range_tags.contains(real_variable_tags[real_index])) {
1176 variables_in_one_gate.erase(real_index);
1177 }
1178 }
1179 }
1180}
1181
1192template <typename FF, typename CircuitBuilder>
1194 size_t gate_index)
1195{
1196
1197 auto find_position = [&](uint32_t real_variable_index) {
1198 return variables_in_one_gate.contains(real_variable_index);
1199 };
1200 std::unordered_set<BasicTableId> aes_plookup_tables{ BasicTableId::AES_SBOX_MAP,
1201 BasicTableId::AES_SPARSE_MAP,
1202 BasicTableId::AES_SPARSE_NORMALIZE };
1203 auto& lookup_block = circuit_builder.blocks.lookup;
1204 if (aes_plookup_tables.contains(table_id)) {
1205 uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]);
1206 uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]);
1207 if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) {
1208 bool find_out = find_position(real_out_idx);
1209 auto q_c = lookup_block.q_c()[gate_index];
1210 if (q_c.is_zero()) {
1211 if (find_out) {
1212 variables_in_one_gate.erase(real_out_idx);
1213 }
1214 }
1215 }
1216 }
1217}
1218
1230template <typename FF, typename CircuitBuilder>
1232 size_t gate_index)
1233{
1234 auto find_position = [&](uint32_t real_variable_index) {
1235 return variables_in_one_gate.contains(real_variable_index);
1236 };
1237 auto& lookup_block = circuit_builder.blocks.lookup;
1238 std::unordered_set<BasicTableId> sha256_plookup_tables{ BasicTableId::SHA256_WITNESS_SLICE_3,
1239 BasicTableId::SHA256_WITNESS_SLICE_7_ROTATE_4,
1240 BasicTableId::SHA256_WITNESS_SLICE_8_ROTATE_7,
1241 BasicTableId::SHA256_WITNESS_SLICE_14_ROTATE_1,
1242 BasicTableId::SHA256_BASE16,
1243 BasicTableId::SHA256_BASE16_ROTATE2,
1244 BasicTableId::SHA256_BASE16_ROTATE6,
1245 BasicTableId::SHA256_BASE16_ROTATE7,
1246 BasicTableId::SHA256_BASE16_ROTATE8,
1247 BasicTableId::SHA256_BASE28,
1248 BasicTableId::SHA256_BASE28_ROTATE3,
1249 BasicTableId::SHA256_BASE28_ROTATE6 };
1250 if (sha256_plookup_tables.contains(table_id)) {
1251 uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]);
1252 uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]);
1253 if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) {
1254 // auto q_m = lookup_block.q_m()[gate_index];
1255 auto q_c = lookup_block.q_c()[gate_index];
1256 bool find_out = find_position(real_out_idx);
1257 // bool find_right = find_position(real_right_idx);
1258 if (q_c.is_zero()) {
1259 if (find_out) {
1260 variables_in_one_gate.erase(real_out_idx);
1261 }
1262 }
1263 if (table_id == SHA256_BASE16_ROTATE2 || table_id == SHA256_BASE28_ROTATE6) {
1264 // we want to remove false cases for special tables even though their selectors != 0
1265 // because they are used in read_from_1_to_2_table function, and they aren't dangerous
1266 variables_in_one_gate.erase(real_out_idx);
1267 }
1268 }
1269 }
1270}
1271
1281template <typename FF, typename CircuitBuilder>
1283{
1284 auto find_position = [&](uint32_t real_variable_index) {
1285 return variables_in_one_gate.contains(real_variable_index);
1286 };
1287 auto& lookup_block = circuit_builder.blocks.lookup;
1288 auto& lookup_tables = circuit_builder.get_lookup_tables();
1289 auto table_index = static_cast<size_t>(static_cast<uint256_t>(lookup_block.q_3()[gate_index]));
1290 for (const auto& table : lookup_tables) {
1291 if (table.table_index == table_index) {
1292 std::unordered_set<bb::fr> column_1(table.column_1.begin(), table.column_1.end());
1293 std::unordered_set<bb::fr> column_2(table.column_2.begin(), table.column_2.end());
1294 std::unordered_set<bb::fr> column_3(table.column_3.begin(), table.column_3.end());
1295 bb::plookup::BasicTableId table_id = table.id;
1296 // false cases for AES
1297 this->remove_unnecessary_aes_plookup_variables(table_id, gate_index);
1298 // false cases for sha256
1299 this->remove_unnecessary_sha256_plookup_variables(table_id, gate_index);
1300 // if the amount of unique elements from columns of plookup tables = 1, it means that
1301 // variable from this column aren't used and we can remove it.
1302 if (column_1.size() == 1) {
1303 uint32_t left_idx = lookup_block.w_l()[gate_index];
1304 uint32_t real_left_idx = this->to_real(left_idx);
1305 bool find_left = find_position(real_left_idx);
1306 if (find_left) {
1307 variables_in_one_gate.erase(real_left_idx);
1308 }
1309 }
1310 if (column_2.size() == 1) {
1311 uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]);
1312 bool find_right = find_position(real_right_idx);
1313 if (find_right) {
1314 variables_in_one_gate.erase(real_right_idx);
1315 }
1316 }
1317 if (column_3.size() == 1) {
1318 uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]);
1319 bool find_out = find_position(real_out_idx);
1320 if (find_out) {
1321 variables_in_one_gate.erase(real_out_idx);
1322 }
1323 }
1324 }
1325 }
1326}
1327
1334template <typename FF, typename CircuitBuilder>
1336{
1337 auto& lookup_block = circuit_builder.blocks.lookup;
1338 if (lookup_block.size() > 0) {
1339 for (size_t i = 0; i < lookup_block.size(); i++) {
1340 this->process_current_plookup_gate(i);
1341 }
1342 }
1343}
1344
1353template <typename FF, typename CircuitBuilder>
1355{
1356 auto block_data = circuit_builder.blocks.get();
1357 if (std::optional<size_t> blk_idx = find_block_index(circuit_builder.blocks.memory); blk_idx) {
1358 std::vector<uint32_t> to_remove;
1359 for (const auto& var_idx : variables_in_one_gate) {
1360 KeyPair key = { var_idx, *blk_idx };
1361 if (auto search = variable_gates.find(key); search != variable_gates.end()) {
1362 std::vector<size_t> gate_indexes = variable_gates[key];
1363 BB_ASSERT_EQ(gate_indexes.size(), 1U);
1364 size_t gate_idx = gate_indexes[0];
1365 auto q_1 = block_data[*blk_idx].q_1()[gate_idx];
1366 auto q_2 = block_data[*blk_idx].q_2()[gate_idx];
1367 auto q_3 = block_data[*blk_idx].q_3()[gate_idx];
1368 auto q_4 = block_data[*blk_idx].q_4()[gate_idx];
1369 auto q_m = block_data[*blk_idx].q_m()[gate_idx];
1370 auto q_arith = block_data[*blk_idx].q_arith()[gate_idx];
1371 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() &&
1372 q_arith.is_zero()) {
1373 // record witness can be in both ROM and RAM gates, so we can ignore q_c
1374 // record witness is written as 4th variable in RAM/ROM read/write gate, so we can get 4th
1375 // wire value and check it with our variable
1376 if (this->to_real(block_data[*blk_idx].w_4()[gate_idx]) == var_idx) {
1377 to_remove.emplace_back(var_idx);
1378 }
1379 }
1380 }
1381 }
1382 for (const auto& elem : to_remove) {
1383 variables_in_one_gate.erase(elem);
1384 }
1385 }
1386}
1387
1395template <typename FF, typename CircuitBuilder>
1397{
1398 variables_in_one_gate.clear();
1399 for (const auto& pair : variables_gate_counts) {
1400 bool is_not_constant_variable = check_is_not_constant_variable(pair.first);
1401 if (pair.second == 1 && pair.first != 0 && is_not_constant_variable) {
1402 variables_in_one_gate.insert(pair.first);
1403 }
1404 }
1405 auto range_lists = circuit_builder.range_lists;
1406 std::unordered_set<uint32_t> decompose_variables;
1407 for (auto& pair : range_lists) {
1408 for (auto& elem : pair.second.variable_indices) {
1409 bool is_not_constant_variable = check_is_not_constant_variable(elem);
1410 if (variables_gate_counts[circuit_builder.real_variable_index[elem]] == 1 && is_not_constant_variable) {
1411 decompose_variables.insert(circuit_builder.real_variable_index[elem]);
1412 }
1413 }
1414 }
1415 remove_unnecessary_decompose_variables(decompose_variables);
1416 remove_unnecessary_plookup_variables();
1417 remove_unnecessary_range_constrains_variables();
1418 for (const auto& elem : fixed_variables) {
1419 variables_in_one_gate.erase(elem);
1420 }
1421 // we found variables that were in one gate and they are intended cases.
1422 // so we have to remove them from the scope
1423 for (const auto& elem : circuit_builder.get_used_witnesses()) {
1424 variables_in_one_gate.erase(elem);
1425 }
1426 remove_record_witness_variables();
1427 return variables_in_one_gate;
1428}
1429
1435template <typename FF, typename CircuitBuilder>
1437{
1438 info("╔═══════╦═══════╦═════════════╦═══════════╦══════════════╗");
1439 info("║ CC# ║ Size ║ Range List ║ Finalize ║ Process ROM ║");
1440 info("╠═══════╬═══════╬═════════════╬═══════════╬══════════════╣");
1441
1442 for (size_t i = 0; i < connected_components.size(); i++) {
1443 const auto& cc = connected_components[i];
1444 std::ostringstream line;
1445
1446 line << "║ " << std::setw(5) << std::right << (i + 1) << " ║ " << std::setw(5) << std::right << cc.size()
1447 << " ║ " << std::setw(11) << std::left << (cc.is_range_list_cc ? "Yes" : "No") << " ║ " << std::setw(9)
1448 << std::left << (cc.is_finalize_cc ? "Yes" : "No") << " ║ " << std::setw(12) << std::left
1449 << (cc.is_process_rom_cc ? "Yes" : "No") << " ║";
1450 info(line.str());
1451 }
1452 info("╚═══════╩═══════╩═════════════╩═══════════╩══════════════╝");
1453 info("Total connected components: ", connected_components.size());
1454}
1455
1462template <typename FF, typename CircuitBuilder> void StaticAnalyzer_<FF, CircuitBuilder>::print_variables_gate_counts()
1463{
1464 for (const auto& it : variables_gate_counts) {
1465 info("number of gates with variables ", it.first, " == ", it.second);
1466 }
1467}
1468
1476template <typename FF, typename CircuitBuilder>
1478{
1479 auto q_arith = block.q_arith()[gate_index];
1480 if (!q_arith.is_zero()) {
1481 info("q_arith == ", q_arith);
1482 // fisrtly, print selectors for standard plonk gate
1483 info("q_m == ", block.q_m()[gate_index]);
1484 info("q1 == ", block.q_1()[gate_index]);
1485 info("q2 == ", block.q_2()[gate_index]);
1486 info("q3 == ", block.q_3()[gate_index]);
1487 info("q4 == ", block.q_4()[gate_index]);
1488 info("q_c == ", block.q_c()[gate_index]);
1489
1490 if (q_arith == FF(2)) {
1491 // we have to print w_4_shift from next gate
1492 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1493 }
1494 if (q_arith == FF(3)) {
1495 // we have to print w_4_shift and w_1_shift from the next gate
1496 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1497 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1498 }
1499 } else {
1500 return;
1501 }
1502}
1503
1511template <typename FF, typename CircuitBuilder>
1513{
1514 auto q_elliptic = block.q_elliptic()[gate_index];
1515 if (!q_elliptic.is_zero()) {
1516 info("q_elliptic == ", q_elliptic);
1517 info("q_1 == ", block.q_1()[gate_index]);
1518 info("q_m == ", block.q_m()[gate_index]);
1519 bool is_elliptic_add_gate = !block.q_1()[gate_index].is_zero() && block.q_m()[gate_index].is_zero();
1520 bool is_elliptic_dbl_gate = block.q_1()[gate_index].is_zero() && block.q_m()[gate_index] == FF::one();
1521 if (is_elliptic_add_gate) {
1522 info("x2 == ", block.w_l()[gate_index + 1]);
1523 info("x3 == ", block.w_r()[gate_index + 1]);
1524 info("y3 == ", block.w_o()[gate_index + 1]);
1525 info("y2 == ", block.w_4()[gate_index + 1]);
1526 }
1527 if (is_elliptic_dbl_gate) {
1528 info("x3 == ", block.w_r()[gate_index + 1]);
1529 info("y3 == ", block.w_o()[gate_index + 1]);
1530 }
1531 } else {
1532 return;
1533 }
1534}
1535
1544template <typename FF, typename CircuitBuilder>
1546{
1547 auto q_lookup = block.q_lookup()[gate_index];
1548 if (!q_lookup.is_zero()) {
1549 info("q_lookup == ", q_lookup);
1550 auto q_2 = block.q_2()[gate_index];
1551 auto q_m = block.q_m()[gate_index];
1552 auto q_c = block.q_c()[gate_index];
1553 info("q_2 == ", q_2);
1554 info("q_m == ", q_m);
1555 info("q_c == ", q_c);
1556 if (!q_2.is_zero()) {
1557 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1558 }
1559 if (!q_m.is_zero()) {
1560 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1561 }
1562 if (!q_c.is_zero()) {
1563 info("w_3_shift == ", block.w_o()[gate_index + 1]);
1564 }
1565 } else {
1566 return;
1567 }
1568}
1569
1578template <typename FF, typename CircuitBuilder>
1580{
1581 auto q_delta_range = block.q_delta_range()[gate_index];
1582 if (!q_delta_range.is_zero()) {
1583 info("q_delta_range == ", q_delta_range);
1584 info("w_1 == ", block.w_l()[gate_index]);
1585 info("w_2 == ", block.w_r()[gate_index]);
1586 info("w_3 == ", block.w_o()[gate_index]);
1587 info("w_4 == ", block.w_4()[gate_index]);
1588 info("w_1_shift == ", block.w_l()[gate_index]);
1589 } else {
1590 return;
1591 }
1592}
1593
1602template <typename FF, typename CircuitBuilder>
1604{
1605 auto internal_selector = block.q_poseidon2_internal()[gate_index];
1606 auto external_selector = block.q_poseidon2_external()[gate_index];
1607 if (!internal_selector.is_zero() || !external_selector.is_zero()) {
1608 info("q_poseidon2_internal == ", internal_selector);
1609 info("q_poseidon2_external == ", external_selector);
1610 info("w_1 == ", block.w_l()[gate_index]);
1611 info("w_2 == ", block.w_r()[gate_index]);
1612 info("w_3 == ", block.w_o()[gate_index]);
1613 info("w_4 == ", block.w_4()[gate_index]);
1614 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1615 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1616 info("w_3_shift == ", block.w_o()[gate_index + 1]);
1617 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1618 } else {
1619 return;
1620 }
1621}
1622
1631template <typename FF, typename CircuitBuilder>
1633{
1634 auto q_nnf = block.q_nnf()[gate_idx];
1635 if (!q_nnf.is_zero()) {
1636 info("q_nnf == ", q_nnf);
1637 auto q_2 = block.q_2()[gate_idx];
1638 auto q_3 = block.q_3()[gate_idx];
1639 auto q_4 = block.q_4()[gate_idx];
1640 auto q_m = block.q_m()[gate_idx];
1641 if (q_3 == FF::one() && q_4 == FF::one()) {
1642 info("w_1_shift == ", block.w_l()[gate_idx + 1]);
1643 info("w_2_shift == ", block.w_r()[gate_idx + 1]);
1644
1645 } else if (q_3 == FF::one() && q_m == FF::one()) {
1646 info("w_1_shift == ", block.w_l()[gate_idx + 1]);
1647 info("w_2_shift == ", block.w_r()[gate_idx + 1]);
1648 info("w_3_shift == ", block.w_o()[gate_idx + 1]);
1649 info("w_4_shift == ", block.w_4()[gate_idx + 1]);
1650 } else if (q_2 == FF::one() && (q_3 == FF::one() || q_4 == FF::one() || q_m == FF::one())) {
1651 info("w_1_shift == ", block.w_l()[gate_idx + 1]);
1652 info("w_2_shift == ", block.w_r()[gate_idx + 1]);
1653 if (q_4 == FF::one() || q_m == FF::one()) {
1654 info("w_3_shift == ", block.w_o()[gate_idx + 1]);
1655 info("w_4_shift == ", block.w_4()[gate_idx + 1]);
1656 }
1657 }
1658 } else {
1659 return;
1660 }
1661}
1662
1671template <typename FF, typename CircuitBuilder>
1673{
1674 auto q_memory = block.q_memory()[gate_index];
1675 if (!q_memory.is_zero()) {
1676 info("q_memory == ", q_memory);
1677 auto q_1 = block.q_1()[gate_index];
1678 auto q_2 = block.q_2()[gate_index];
1679 auto q_3 = block.q_3()[gate_index];
1680 auto q_4 = block.q_4()[gate_index];
1681 if (q_1 == FF::one() && q_4 == FF::one()) {
1682 info("q_1 == ", q_1);
1683 info("q_4 == ", q_4);
1684 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1685 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1686 } else if (q_1 == FF::one() && q_2 == FF::one()) {
1687 info("q_1 == ", q_1);
1688 info("q_2 == ", q_2);
1689 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1690 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1691 } else if (!q_3.is_zero()) {
1692 info("q_3 == ", q_3);
1693 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1694 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1695 info("w_3_shift == ", block.w_o()[gate_index + 1]);
1696 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1697 }
1698 } else {
1699 return;
1700 }
1701}
1702
1710template <typename FF, typename CircuitBuilder>
1712{
1713 const auto& block_data = circuit_builder.blocks.get();
1714 for (const auto& [key, gates] : variable_gates) {
1715 if (key.first == real_idx) {
1716 for (size_t i = 0; i < gates.size(); i++) {
1717 size_t gate_index = gates[i];
1718 auto& block = block_data[key.second];
1719 info("---- printing variables in this gate");
1720 info("w_l == ",
1721 block.w_l()[gate_index],
1722 " w_r == ",
1723 block.w_r()[gate_index],
1724 " w_o == ",
1725 block.w_o()[gate_index],
1726 " w_4 == ",
1727 block.w_4()[gate_index]);
1728 info("---- printing gate info where variable with index ", key.first, " was found ----");
1729 print_arithmetic_gate_info(gate_index, block);
1730 print_elliptic_gate_info(gate_index, block);
1731 print_plookup_gate_info(gate_index, block);
1732 print_poseidon2s_gate_info(gate_index, block);
1733 print_delta_range_gate_info(gate_index, block);
1734 print_nnf_gate_info(gate_index, block);
1735 print_memory_gate_info(gate_index, block);
1736 if constexpr (IsMegaBuilder<CircuitBuilder>) {
1737 auto q_databus = block.q_busread()[gate_index];
1738 if (!q_databus.is_zero()) {
1739 info("q_databus == ", q_databus);
1740 }
1741 }
1742 info("---- finished printing ----");
1743 }
1744 }
1745 }
1746}
1747
1757template <typename FF, typename CircuitBuilder>
1759 analyze_circuit(bool filter_cc)
1760{
1761 auto variables_in_one_gate = get_variables_in_one_gate();
1762 find_connected_components();
1763 if (filter_cc) {
1764 std::vector<ConnectedComponent> main_connected_components;
1765 main_connected_components.reserve(connected_components.size());
1766 for (auto& cc : connected_components) {
1767 if (!cc.is_range_list_cc && !cc.is_finalize_cc && !cc.is_process_rom_cc) {
1768 main_connected_components.emplace_back(cc);
1769 }
1770 }
1771 return std::make_pair(std::move(main_connected_components), std::move(variables_in_one_gate));
1772 }
1773 return std::make_pair(connected_components, std::move(variables_in_one_gate));
1774}
1775
1778
1779} // namespace cdg
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
std::vector< uint32_t > real_variable_index
Map from witness index to real variable index.
TranslatorCircuitBuilder creates a circuit that evaluates the correctness of the evaluation of EccOpQ...
void print_delta_range_gate_info(size_t gate_idx, auto &block)
this method prints all information about range constrain gate where variable was found
Definition graph.cpp:1579
void process_execution_trace()
Definition graph.cpp:647
void print_memory_gate_info(size_t gate_idx, auto &block)
this method prints all information about memory gate where variable was found
Definition graph.cpp:1672
void print_plookup_gate_info(size_t gate_idx, auto &block)
this method prints all information about plookup gate where variable was found
Definition graph.cpp:1545
std::vector< uint32_t > get_ram_table_connected_component(const bb::RamTranscript &ram_array)
this method gets the RAM table connected component by processing RAM transcript records
Definition graph.cpp:534
std::unordered_map< uint32_t, std::vector< uint32_t > > variable_adjacency_lists
Definition graph.hpp:168
bool is_gate_sorted_rom(size_t memory_block_idx, size_t gate_idx) const
this method checks if current gate is sorted ROM gate
Definition graph.cpp:929
std::vector< uint32_t > get_eccop_part_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from elliptic curve operation gates
Definition graph.cpp:614
std::vector< uint32_t > get_memory_gate_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from Memory gates (RAM and ROM consistency checks)
Definition graph.cpp:331
std::vector< uint32_t > get_plookup_gate_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from plookup gates
Definition graph.cpp:255
void remove_unnecessary_decompose_variables(const std::unordered_set< uint32_t > &decompose_variables)
this method removes unnecessary variables from decompose chains
Definition graph.cpp:1105
std::vector< ConnectedComponent > find_connected_components()
this methond finds all connected components in the graph described by adjacency lists and marks some ...
Definition graph.cpp:897
void depth_first_search(const uint32_t &variable_index, std::unordered_set< uint32_t > &is_used, std::vector< uint32_t > &connected_component)
this method implements depth-first search algorithm for undirected graphs
Definition graph.cpp:868
bool check_is_not_constant_variable(const uint32_t &variable_index)
this method checks whether the variable with given index is not constant
Definition graph.cpp:797
std::vector< uint32_t > get_arithmetic_gate_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from arithmetic gates
Definition graph.cpp:80
void remove_unnecessary_sha256_plookup_variables(bb::plookup::BasicTableId &table_id, size_t gate_index)
this method removes false cases in sha256 lookup tables. tables which are enumerated in the unordered...
Definition graph.cpp:1231
std::unordered_set< uint32_t > get_variables_in_one_gate()
this method returns a final set of variables that were in one gate
Definition graph.cpp:1396
std::vector< uint32_t > get_non_native_field_gate_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from Non-Native Field gates (bigfield operations)
Definition graph.cpp:391
void remove_record_witness_variables()
this method removes record witness variables from variables in one gate. initially record witness is ...
Definition graph.cpp:1354
void print_variable_info(const uint32_t real_idx)
this method prints all information about gates where variable was found
Definition graph.cpp:1711
void remove_unnecessary_range_constrains_variables()
this method removes variables from range constraints that are not security critical
Definition graph.cpp:1155
std::pair< std::vector< ConnectedComponent >, std::unordered_set< uint32_t > > analyze_circuit(bool filter_cc=true)
this functions was made for more convenient testing process
Definition graph.cpp:1759
void print_elliptic_gate_info(size_t gate_idx, auto &block)
this method prints all information about elliptic gate where variable was found
Definition graph.cpp:1512
StaticAnalyzer_()=default
std::vector< uint32_t > get_databus_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from databus gates
Definition graph.cpp:589
void connect_all_variables_in_vector(const std::vector< uint32_t > &variables_vector)
this method connects 2 variables if they are in one gate and 1) have different indices,...
Definition graph.cpp:814
void print_connected_components_info()
this method prints additional information about connected components that were found in the graph
Definition graph.cpp:1436
std::vector< uint32_t > get_rom_table_connected_component(const bb::RomTranscript &rom_array)
this method gets the ROM table connected component by processing ROM transcript records
Definition graph.cpp:471
std::vector< uint32_t > get_poseido2s_gate_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from poseidon2 gates
Definition graph.cpp:296
void print_poseidon2s_gate_info(size_t gate_idx, auto &block)
this method prints all information about poseidon2s gate where variable was found
Definition graph.cpp:1603
std::unordered_map< uint32_t, size_t > variables_gate_counts
Definition graph.hpp:171
std::vector< uint32_t > get_sort_constraint_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from sorted constraints
Definition graph.cpp:217
void save_constant_variable_indices()
this method needs to save all constant variables indices in one data structure in order to not go thr...
Definition graph.cpp:780
std::optional< size_t > find_block_index(const auto &block)
this method finds index of the block in circuit builder by comparing pointers to blocks
Definition graph.cpp:24
bool variable_only_in_sorted_rom_gates(uint32_t var_idx, size_t blk_idx) const
this method checks that every gate for given variable in a given block is sorted ROM gate
Definition graph.cpp:946
void remove_unnecessary_aes_plookup_variables(bb::plookup::BasicTableId &table_id, size_t gate_index)
this method removes false positive cases variables from aes plookup tables. AES_SBOX_MAP,...
Definition graph.cpp:1193
void process_gate_variables(std::vector< uint32_t > &gate_variables, size_t gate_index, size_t blk_idx)
this method processes variables from a gate by removing duplicates and updating tracking structures
Definition graph.cpp:50
CircuitBuilder & circuit_builder
Definition graph.hpp:164
void remove_unnecessary_plookup_variables()
this method removes false cases plookup variables from variables in one gate
Definition graph.cpp:1335
std::vector< uint32_t > get_elliptic_gate_connected_component(size_t index, size_t block_idx, auto &blk)
this method creates connected components from elliptic gates
Definition graph.cpp:165
void print_nnf_gate_info(size_t gate_idx, auto &block)
this method prints all information about non natife field gate where variable was found
Definition graph.cpp:1632
void print_arithmetic_gate_info(size_t gate_idx, auto &block)
this method prints all information about arithmetic gate where variable was found
Definition graph.cpp:1477
void process_current_plookup_gate(size_t gate_index)
this method removes false cases in lookup table for a given gate. it uses all functions above for loo...
Definition graph.cpp:1282
void mark_range_list_connected_components()
this method marks some connected componets like they represent range lists tool needs this method to ...
Definition graph.cpp:996
void print_variables_gate_counts()
this method prints a number of gates for each variable
Definition graph.cpp:1462
void mark_process_rom_connected_component()
this method marks some connected components if they were created by function process_rom_array....
Definition graph.cpp:970
std::unordered_map< uint32_t, size_t > variables_degree
Definition graph.hpp:173
size_t process_current_decompose_chain(size_t index)
this method removes variables that were created in a function decompose_into_default_range because th...
Definition graph.cpp:1052
void add_new_edge(const uint32_t &first_variable_index, const uint32_t &second_variable_index)
this method creates an edge between two variables in graph. All needed checks in a function above
Definition graph.cpp:849
void mark_finalize_connected_components()
this method marks some connected components like they represent separated finalize blocks the point i...
Definition graph.cpp:1024
void info(Args... args)
Definition log.hpp:75
@ SHA256_BASE16_ROTATE2
Definition types.hpp:37
@ SHA256_BASE28_ROTATE6
Definition types.hpp:34
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
Definition graph.cpp:14
std::pair< uint32_t, size_t > KeyPair
Definition graph.hpp:27
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
RamTranscript contains the RamRecords for a particular RAM table (recording READ and WRITE operations...
std::vector< RamRecord > records
RomTranscript contains the RomRecords for a particular ROM table as well as the vector whose ith entr...
std::vector< RomRecord > records
BB_INLINE constexpr bool is_zero() const noexcept