Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
graph.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: Planned, auditors: [], commit: }
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
5// =====================
6
7#include "./graph.hpp"
8#include "./gate_patterns.hpp"
12#include <algorithm>
13#include <array>
14#include <iomanip>
15#include <optional>
16#include <stack>
17
18using namespace bb::plookup;
19using namespace bb;
20
21namespace cdg {
22
37template <typename FF, typename CircuitBuilder>
38inline void StaticAnalyzer_<FF, CircuitBuilder>::process_gate_variables(std::vector<uint32_t>& gate_variables,
39 size_t gate_index,
40 auto& blk)
41{
42 auto unique_variables = std::unique(gate_variables.begin(), gate_variables.end());
43 gate_variables.erase(unique_variables, gate_variables.end());
44 if (gate_variables.empty()) {
45 return;
46 }
47 for (auto& var_idx : gate_variables) {
48 KeyPair key = std::make_pair(var_idx, &blk);
49 variable_gates[key].emplace_back(gate_index);
50 }
51 for (const auto& variable_index : gate_variables) {
52 variables_gate_counts[variable_index] += 1;
53 }
54}
55
68template <typename FF, typename CircuitBuilder>
69template <typename Block>
71 size_t index, Block& blk, const bb::gate_patterns::GatePattern& pattern, bb::GateKind kind)
72{
73 using namespace bb::gate_patterns;
74
75 if (read_gate_selector(blk, kind, index).is_zero()) {
76 return {};
77 }
78
79 // Read selectors and extract wire indices using the pattern
80 Selectors selectors = read_selectors(blk, index, kind);
81 std::vector<uint32_t> gate_variables = extract_wires(blk, index, pattern, selectors);
82
83 // Convert to real indices and process
84 gate_variables = to_real(gate_variables);
85 process_gate_variables(gate_variables, index, blk);
86 return gate_variables;
87}
88
96template <typename FF, typename CircuitBuilder>
98 const bb::RomTranscript& rom_array)
99{
100 // Every RomTranscript data structure has 2 main components that are interested for static analyzer:
101 // 1) records contains values that were put in the gate, we can use them to create connections between variables
102 // 2) states contains values witness indexes that we can find in the ROM record in the RomTrascript, so we can
103 // ignore state of the ROM transcript, because we still can connect all variables using variables from records.
104 std::vector<uint32_t> rom_table_variables;
105 auto& memory_block = circuit_builder.blocks.memory;
106 for (const auto& record : rom_array.records) {
107 std::vector<uint32_t> gate_variables;
108 size_t gate_index = record.gate_index;
109
110 auto q_1 = memory_block.q_1()[gate_index];
111 auto q_2 = memory_block.q_2()[gate_index];
112 auto q_3 = memory_block.q_3()[gate_index];
113 auto q_4 = memory_block.q_4()[gate_index];
114 auto q_m = memory_block.q_m()[gate_index];
115 auto q_c = memory_block.q_c()[gate_index];
116
117 auto index_witness = record.index_witness;
118 auto vc1_witness = record.value_column1_witness; // state[0] from RomTranscript
119 auto vc2_witness = record.value_column2_witness; // state[1] from RomTranscript
120 auto record_witness = record.record_witness;
121
122 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() && q_c.is_zero()) {
123 // By default ROM read gate uses variables (w_1, w_2, w_3, w_4) = (index_witness, vc1_witness,
124 // vc2_witness, record_witness) So we can update all of them
125 gate_variables.emplace_back(index_witness);
126 if (vc1_witness != circuit_builder.zero_idx()) {
127 gate_variables.emplace_back(vc1_witness);
128 }
129 if (vc2_witness != circuit_builder.zero_idx()) {
130 gate_variables.emplace_back(vc2_witness);
131 }
132 gate_variables.emplace_back(record_witness);
133 } else if (q_1.is_zero() && q_3.is_zero() && q_m.is_zero() &&
134 ((q_2 == FF::one() && q_4.is_zero()) || (q_4 == FF::one() && q_2.is_zero()))) {
135 // ROM-LogUp gate: (w_1, w_2, w_3, w_4) = (index, value, multiplicity-or-zero, inverse). The
136 // multiplicity (table rows) and inverse are internal and excused via update_used_witnesses; here we
137 // connect the index and value so the ACIR-visible witnesses join the ROM component. Table entries
138 // set q_2, read accesses set q_4.
139 gate_variables.emplace_back(index_witness);
140 if (vc1_witness != circuit_builder.zero_idx()) {
141 gate_variables.emplace_back(vc1_witness);
142 }
143 }
144 gate_variables = to_real(gate_variables);
145 process_gate_variables(gate_variables, gate_index, memory_block);
146 // after process_gate_variables function gate_variables constists of real variables indexes, so we can
147 // add all this variables in the final vector to connect all of them
148 if (!gate_variables.empty()) {
149 rom_table_variables.insert(rom_table_variables.end(), gate_variables.begin(), gate_variables.end());
150 }
151 }
152 return rom_table_variables;
153}
154
162template <typename FF, typename CircuitBuilder>
164 const bb::RamTranscript& ram_array)
165{
166 std::vector<uint32_t> ram_table_variables;
167 auto& memory_block = circuit_builder.blocks.memory;
168 for (const auto& record : ram_array.records) {
169 std::vector<uint32_t> gate_variables;
170 size_t gate_index = record.gate_index;
171
172 auto q_1 = memory_block.q_1()[gate_index];
173 auto q_2 = memory_block.q_2()[gate_index];
174 auto q_3 = memory_block.q_3()[gate_index];
175 auto q_4 = memory_block.q_4()[gate_index];
176 auto q_m = memory_block.q_m()[gate_index];
177 auto q_c = memory_block.q_c()[gate_index];
178
179 auto index_witness = record.index_witness;
180 auto timestamp_witness = record.timestamp_witness;
181 auto value_witness = record.value_witness;
182 auto record_witness = record.record_witness;
183
184 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() &&
185 (q_c.is_zero() || q_c == FF::one())) {
186 // By default RAM read/write gate uses variables (w_1, w_2, w_3, w_4) = (index_witness,
187 // timestamp_witness, value_witness, record_witness) So we can update all of them
188 gate_variables.emplace_back(index_witness);
189 if (timestamp_witness != circuit_builder.zero_idx()) {
190 gate_variables.emplace_back(timestamp_witness);
191 }
192 if (value_witness != circuit_builder.zero_idx()) {
193 gate_variables.emplace_back(value_witness);
194 }
195 gate_variables.emplace_back(record_witness);
196 }
197 gate_variables = to_real(gate_variables);
198 process_gate_variables(gate_variables, gate_index, memory_block);
199 // after process_gate_variables function gate_variables constists of real variables indexes, so we can add
200 // all these variables in the final vector to connect all of them
201 ram_table_variables.insert(ram_table_variables.end(), gate_variables.begin(), gate_variables.end());
202 }
203 return ram_table_variables;
204}
205
217template <typename FF, typename CircuitBuilder>
219 auto& blk)
220{
221 std::vector<uint32_t> gate_variables;
222
223 // Only process gates in the ecc_op block, otherwise return early
224 if constexpr (IsMegaBuilder<CircuitBuilder>) {
225 if (&blk != &circuit_builder.blocks.ecc_op) {
226 return gate_variables;
227 }
228 }
229
230 std::vector<uint32_t> first_row_variables;
231 std::vector<uint32_t> second_row_variables;
232 auto w1 = blk.w_l()[index]; // get opcode of operation, because function get_ecc_op_idx returns type
233 // uint32_t and it adds as w1
234 if (w1 != circuit_builder.zero_idx()) {
235 // this is opcode and start of the UltraOp element
236 first_row_variables.insert(
237 first_row_variables.end(),
238 { w1, blk.w_r()[index], blk.w_o()[index], blk.w_4()[index] }); // add op, x_lo, x_hi, y_lo
239 if (index < blk.size() - 1) {
240 second_row_variables.insert(
241 second_row_variables.end(),
242 { blk.w_r()[index + 1], blk.w_o()[index + 1], blk.w_4()[index + 1] }); // add y_hi, z1, z2
243 }
244 first_row_variables = to_real(first_row_variables);
245 second_row_variables = to_real(second_row_variables);
246 process_gate_variables(first_row_variables, index, blk);
247 process_gate_variables(second_row_variables, index, blk);
248 }
249 if (!first_row_variables.empty()) {
250 gate_variables.insert(gate_variables.end(), first_row_variables.cbegin(), first_row_variables.cend());
251 }
252 if (!second_row_variables.empty()) {
253 gate_variables.insert(gate_variables.end(), second_row_variables.cbegin(), second_row_variables.cend());
254 }
255 return gate_variables;
256}
257
258template <typename FF, typename CircuitBuilder> void StaticAnalyzer_<FF, CircuitBuilder>::process_execution_trace()
259{
260 using namespace bb::gate_patterns;
261
262 for (auto& blk : circuit_builder.blocks.get()) {
263 if (blk.size() == 0 || &blk == &circuit_builder.blocks.pub_inputs) {
264 continue;
265 }
266
267 std::vector<uint32_t> eccop_variables;
268 for (size_t gate_idx = 0; gate_idx < blk.size(); gate_idx++) {
269 // Try each pattern until one matches (returns non-empty)
270 std::vector<uint32_t> cc;
271 auto try_pattern = [&](const GatePattern& pattern, GateKind kind) {
272 if (cc.empty()) {
273 cc = extract_gate_variables(gate_idx, blk, pattern, kind);
274 }
275 };
276
277 // Standard gate patterns (mutually exclusive - at most one will match)
278 try_pattern(ARITHMETIC, GateKind::Arith);
279 try_pattern(ELLIPTIC, GateKind::Elliptic);
280 try_pattern(LOOKUP, GateKind::Lookup);
281 try_pattern(POSEIDON2_EXTERNAL, GateKind::Poseidon2Ext);
282 if constexpr (IsMegaBuilder<CircuitBuilder>) {
283 try_pattern(POSEIDON2_QUAD_INTERNAL, GateKind::Poseidon2QuadInt);
284 try_pattern(POSEIDON2_QUAD_INTERNAL_TERMINAL, GateKind::Poseidon2QuadIntTerminal);
285 try_pattern(POSEIDON2_TRANSITION_ENTRY, GateKind::Poseidon2TransitionEntry);
286 try_pattern(POSEIDON2_INITIAL_EXTERNAL, GateKind::Poseidon2ExtInitial);
287 } else {
288 try_pattern(POSEIDON2_INTERNAL, GateKind::Poseidon2Int);
289 }
290 try_pattern(NON_NATIVE_FIELD, GateKind::Nnf);
291 try_pattern(MEMORY, GateKind::Memory); // consistency gates only; access gates via ROM/RAM transcripts
292 try_pattern(DELTA_RANGE, GateKind::DeltaRange);
293
294 if (!cc.empty() && connect_variables) {
295 connect_all_variables_in_vector(cc);
296 }
297
298 // MegaBuilder-specific patterns
299 if constexpr (IsMegaBuilder<CircuitBuilder>) {
300 auto databus_cc = extract_gate_variables(gate_idx, blk, DATABUS, GateKind::BusRead);
301 if (!databus_cc.empty() && connect_variables) {
302 connect_all_variables_in_vector(databus_cc);
303 }
304
305 // Bilinear / batched-eq gate (shares the arithmetic block; q_arith and
306 // q_bilinear_batched_eq are mutually exclusive). BILINEAR mode is one equation over the four
307 // wires, so they form a single connected group; BATCHED_EQ mode holds two independent
308 // equalities, so each half is connected separately.
309 auto bilinear_cc = extract_gate_variables(gate_idx, blk, BILINEAR, GateKind::BilinearBatchedEq);
310 if (!bilinear_cc.empty() && connect_variables) {
311 connect_all_variables_in_vector(bilinear_cc);
312 }
313 auto batched_eq_half_1_cc =
314 extract_gate_variables(gate_idx, blk, BATCHED_EQ_HALF_1, GateKind::BilinearBatchedEq);
315 if (!batched_eq_half_1_cc.empty() && connect_variables) {
316 connect_all_variables_in_vector(batched_eq_half_1_cc);
317 }
318 auto batched_eq_half_2_cc =
319 extract_gate_variables(gate_idx, blk, BATCHED_EQ_HALF_2, GateKind::BilinearBatchedEq);
320 if (!batched_eq_half_2_cc.empty() && connect_variables) {
321 connect_all_variables_in_vector(batched_eq_half_2_cc);
322 }
323
324 auto eccop_cc = get_eccop_part_connected_component(gate_idx, blk);
325 if (!eccop_cc.empty() && connect_variables) {
326 eccop_variables.insert(eccop_variables.end(), eccop_cc.begin(), eccop_cc.end());
327 if (eccop_cc[0] == circuit_builder.equality_op_idx) {
328 connect_all_variables_in_vector(eccop_variables);
329 eccop_variables.clear();
330 }
331 }
332 }
333 }
334 }
335
336 const auto& rom_arrays = circuit_builder.rom_ram_logic.rom_arrays;
337 if (!rom_arrays.empty()) {
338 for (const auto& rom_array : rom_arrays) {
339 std::vector<uint32_t> variable_indices = get_rom_table_connected_component(rom_array);
340 if (connect_variables) {
341 connect_all_variables_in_vector(variable_indices);
342 }
343 }
344 }
345
346 const auto& ram_arrays = circuit_builder.rom_ram_logic.ram_arrays;
347 if (!ram_arrays.empty()) {
348 for (const auto& ram_array : ram_arrays) {
349 std::vector<uint32_t> variable_indices = get_ram_table_connected_component(ram_array);
350 if (connect_variables) {
351 connect_all_variables_in_vector(variable_indices);
352 }
353 }
354 }
355}
356
379template <typename FF, typename CircuitBuilder>
381 : circuit_builder(circuit_builder)
382 , connect_variables(connect_variables)
383{
384 variables_gate_counts = std::unordered_map<uint32_t, size_t>(circuit_builder.real_variable_index.size());
387 variables_degree = std::unordered_map<uint32_t, size_t>(circuit_builder.real_variable_index.size());
388 for (const auto& variable_index : circuit_builder.real_variable_index) {
389 variables_gate_counts[variable_index] = 0;
390 variables_degree[variable_index] = 0;
391 variable_adjacency_lists[variable_index] = {};
392 }
395}
396
405template <typename FF, typename CircuitBuilder>
407{
408 constant_variable_indices_set.clear();
409 const auto& constant_variable_indices = circuit_builder.constant_variable_indices;
410 for (const auto& pair : constant_variable_indices) {
411 constant_variable_indices_set.insert(pair.second);
412 }
413}
414
422template <typename FF, typename CircuitBuilder>
424{
425 uint32_t real_variable_index = circuit_builder.real_variable_index[variable_index];
426 return constant_variable_indices_set.find(real_variable_index) == constant_variable_indices_set.end();
427}
428
439template <typename FF, typename CircuitBuilder>
440void StaticAnalyzer_<FF, CircuitBuilder>::connect_all_variables_in_vector(const std::vector<uint32_t>& variables_vector)
441{
442 if (variables_vector.empty()) {
443 return;
444 }
445 std::vector<uint32_t> filtered_variables_vector;
446 filtered_variables_vector.reserve(variables_vector.size());
447 // Only copy non-zero and non-constant variables
448 std::copy_if(variables_vector.begin(),
449 variables_vector.end(),
450 std::back_inserter(filtered_variables_vector),
451 [&](uint32_t variable_index) {
452 return variable_index != circuit_builder.zero_idx() &&
453 this->check_is_not_constant_variable(variable_index);
454 });
455 // Remove duplicates
456 auto unique_pointer = std::unique(filtered_variables_vector.begin(), filtered_variables_vector.end());
457 filtered_variables_vector.erase(unique_pointer, filtered_variables_vector.end());
458 if (filtered_variables_vector.size() < 2) {
459 return;
460 }
461 for (size_t i = 0; i < filtered_variables_vector.size() - 1; i++) {
462 add_new_edge(filtered_variables_vector[i], filtered_variables_vector[i + 1]);
463 }
464}
465
474template <typename FF, typename CircuitBuilder>
475void StaticAnalyzer_<FF, CircuitBuilder>::add_new_edge(const uint32_t& first_variable_index,
476 const uint32_t& second_variable_index)
477{
478 variable_adjacency_lists[first_variable_index].emplace_back(second_variable_index);
479 variable_adjacency_lists[second_variable_index].emplace_back(first_variable_index);
480 variables_degree[first_variable_index] += 1;
481 variables_degree[second_variable_index] += 1;
482}
483
493template <typename FF, typename CircuitBuilder>
495 std::unordered_set<uint32_t>& is_used,
496 std::vector<uint32_t>& connected_component)
497{
498 std::stack<uint32_t> variable_stack;
499 variable_stack.push(variable_index);
500 while (!variable_stack.empty()) {
501 uint32_t current_index = variable_stack.top();
502 variable_stack.pop();
503 if (!is_used.contains(current_index)) {
504 is_used.insert(current_index);
505 connected_component.emplace_back(current_index);
506 for (const auto& it : variable_adjacency_lists[current_index]) {
507 variable_stack.push(it);
508 }
509 }
510 }
511}
512
522template <typename FF, typename CircuitBuilder>
524{
525 if (!connect_variables) {
526 throw_or_abort("find_connected_components() can only be called when connect_variables is true");
527 }
528 connected_components.clear();
529 std::unordered_set<uint32_t> visited;
530 for (const auto& pair : variable_adjacency_lists) {
531 if (pair.first != 0 && variables_degree[pair.first] > 0) {
532 if (!visited.contains(pair.first)) {
533 std::vector<uint32_t> variable_indices;
534 depth_first_search(pair.first, visited, variable_indices);
535 std::sort(variable_indices.begin(), variable_indices.end());
536 connected_components.emplace_back(ConnectedComponent(variable_indices));
537 }
538 }
539 }
540 mark_range_list_connected_components();
541 mark_finalize_connected_components();
542 mark_process_rom_connected_component();
543 return connected_components;
544}
545
554template <typename FF, typename CircuitBuilder>
555bool StaticAnalyzer_<FF, CircuitBuilder>::is_gate_sorted_rom(auto& memory_block, size_t gate_idx) const
556{
557 return memory_block.gate_selector_for(GateKind::Memory)[gate_idx] == FF::one() &&
558 memory_block.q_1()[gate_idx] == FF::one() && memory_block.q_2()[gate_idx] == FF::one();
559}
560
569template <typename FF, typename CircuitBuilder>
571{
572 bool result = false;
573 KeyPair key = { var_idx, &blk };
574 auto it = variable_gates.find(key);
575 if (it != variable_gates.end()) {
576 const auto& gates = it->second;
578 gates.begin(), gates.end(), [this, &blk](size_t gate_idx) { return is_gate_sorted_rom(blk, gate_idx); });
579 }
580 return result;
581}
582
592template <typename FF, typename CircuitBuilder>
594{
595 auto& memory_block = circuit_builder.blocks.memory;
596 for (auto& cc : connected_components) {
597 const std::vector<uint32_t>& variables = cc.vars();
598 cc.is_process_rom_cc =
599 std::all_of(variables.begin(), variables.end(), [this, &memory_block](uint32_t real_var_idx) {
600 return variable_only_in_sorted_rom_gates(real_var_idx, memory_block);
601 });
602 }
603}
604
614template <typename FF, typename CircuitBuilder>
616{
617 const auto& tags = circuit_builder.real_variable_tags;
618 std::unordered_set<uint32_t> tau_tags;
619 for (const auto& pair : circuit_builder.range_lists) {
620 tau_tags.insert(pair.second.tau_tag);
621 }
622 for (auto& cc : connected_components) {
623 const auto& variables = cc.variable_indices;
624 const uint32_t first_tag = tags[variables[0]];
625 if (tau_tags.contains(first_tag)) {
626 cc.is_range_list_cc =
627 std::all_of(variables.begin() + 1, variables.end(), [&tags, first_tag](uint32_t var_idx) {
628 return tags[var_idx] == first_tag;
629 });
630 }
631 }
632}
633
642template <typename FF, typename CircuitBuilder>
644{
645 const auto& finalize_witnesses = circuit_builder.get_finalize_witnesses();
646 for (auto& cc : connected_components) {
647 const auto& vars = cc.vars();
648 cc.is_finalize_cc = std::all_of(vars.begin(), vars.end(), [&finalize_witnesses](uint32_t var_idx) {
649 return finalize_witnesses.contains(var_idx);
650 });
651 }
652}
653
670template <typename FF, typename CircuitBuilder>
672{
673 auto& arithmetic_block = circuit_builder.blocks.arithmetic;
674 auto zero_idx = circuit_builder.zero_idx();
675 size_t current_index = index;
676 std::vector<uint32_t> accumulators_indices;
677 while (true) {
678 // we have to remove left, right and output wires of the current gate, cause they'are new_limbs, and they
679 // are useless for the analyzer
680 auto fourth_idx = arithmetic_block.w_4()[current_index];
681 accumulators_indices.emplace_back(this->to_real(fourth_idx));
682 auto left_idx = arithmetic_block.w_l()[current_index];
683 if (left_idx != zero_idx) {
684 variables_in_one_gate.erase(this->to_real(left_idx));
685 }
686 auto right_idx = arithmetic_block.w_r()[current_index];
687 if (right_idx != zero_idx) {
688 variables_in_one_gate.erase(this->to_real(right_idx));
689 }
690 auto out_idx = arithmetic_block.w_o()[current_index];
691 if (out_idx != zero_idx) {
692 variables_in_one_gate.erase(this->to_real(out_idx));
693 }
694 auto q_arith = arithmetic_block.gate_selector_for(GateKind::Arith)[current_index];
695 if (q_arith == 1 || current_index == arithmetic_block.size() - 1) {
696 // this is the last gate in this chain, or we can't go next, so we have to stop a loop
697 break;
698 }
699 current_index++;
700 }
701 for (size_t i = 0; i < accumulators_indices.size(); i++) {
702 if (i == 0) {
703 // the first variable in accumulators is the variable which decompose was created. So, we have to
704 // decrement variable_gate_counts for this variable
705 variables_gate_counts[accumulators_indices[i]] -= 1;
706 } else {
707 // next accumulators are useless variables that are not interested for the analyzer. So, for these
708 // variables we can nullify variables_gate_counts
709 variables_gate_counts[accumulators_indices[i]] = 0;
710 }
711 }
712 // we don't want to make variables_gate_counts for intermediate variables negative, so, can go to the next gates
713 return current_index;
714}
715
723template <typename FF, typename CircuitBuilder>
725 const std::unordered_set<uint32_t>& decompose_variables)
726{
727 auto is_power_two = [&](const uint256_t& number) { return number > 0 && ((number & (number - 1)) == 0); };
728 auto find_position = [&](uint32_t variable_index) {
729 return decompose_variables.contains(this->to_real(variable_index));
730 };
731 auto& arithmetic_block = circuit_builder.blocks.arithmetic;
732 if (arithmetic_block.size() > 0) {
733 for (size_t i = 0; i < arithmetic_block.size(); i++) {
734 auto q_1 = arithmetic_block.q_1()[i];
735 auto q_2 = arithmetic_block.q_2()[i];
736 auto q_3 = arithmetic_block.q_3()[i];
737 // big addition gate from decompose has selectors, which have the next property:
738 // q_1 = (1) << shifts[0], target_range_bitnum * (3 * i),
739 // q_2 = (1) << shifts[1], target_range_bitnum * (3 * i + 1),
740 // q_3 = (1) << shifts[2], target_range_bitnum * (3 * i + 2)
741 // so, they are power of two and satisfying the following equality: q_2 * q_2 = q_1 * q_3
742 // this way we can differ them from other arithmetic gates
743 bool q_1_is_power_two = is_power_two(q_1);
744 bool q_2_is_power_two = is_power_two(q_2);
745 bool q_3_is_power_two = is_power_two(q_3);
746 if (q_2 * q_2 == q_1 * q_3 && q_1_is_power_two && q_2_is_power_two && q_3_is_power_two) {
747 uint32_t left_idx = arithmetic_block.w_l()[i];
748 uint32_t right_idx = arithmetic_block.w_r()[i];
749 uint32_t out_idx = arithmetic_block.w_o()[i];
750 uint32_t fourth_idx = arithmetic_block.w_4()[i];
751 bool find_left = find_position(left_idx);
752 bool find_right = find_position(right_idx);
753 bool find_out = find_position(out_idx);
754 bool find_fourth = find_position(fourth_idx);
755 if (((find_left && find_right && find_out) || (find_left && find_right && !find_out) ||
756 (find_left && find_right && !find_out) || (find_left && !find_right && !find_out)) &&
757 !find_fourth) {
758 i = this->process_current_decompose_chain(i);
759 }
760 }
761 }
762 }
763}
764
773template <typename FF, typename CircuitBuilder>
775{
776 const auto& range_lists = circuit_builder.range_lists;
777 std::unordered_set<uint32_t> range_lists_tau_tags;
778 std::unordered_set<uint32_t> range_lists_range_tags;
779 const auto& real_variable_tags = circuit_builder.real_variable_tags;
780 for (const auto& pair : range_lists) {
781 typename CircuitBuilder::RangeList list = pair.second;
782 range_lists_tau_tags.insert(list.tau_tag);
783 range_lists_range_tags.insert(list.range_tag);
784 }
785 for (uint32_t real_index = 0; real_index < real_variable_tags.size(); real_index++) {
786 if (variables_in_one_gate.contains(real_index)) {
787 // this if helps us to remove variables from delta_range_constraints when finalize_circuit() function
788 // was called
789 if (range_lists_tau_tags.contains(real_variable_tags[real_index])) {
790 variables_in_one_gate.erase(real_index);
791 }
792 // this if helps us to remove variables from range_constraints when range_constraint_into_two_limbs
793 // function was called
794 if (range_lists_range_tags.contains(real_variable_tags[real_index])) {
795 variables_in_one_gate.erase(real_index);
796 }
797 }
798 }
799}
800
811template <typename FF, typename CircuitBuilder>
813 size_t gate_index)
814{
815
816 auto find_position = [&](uint32_t real_variable_index) {
817 return variables_in_one_gate.contains(real_variable_index);
818 };
819 std::unordered_set<BasicTableId> aes_plookup_tables{ BasicTableId::AES_SBOX_MAP,
820 BasicTableId::AES_SPARSE_MAP,
821 BasicTableId::AES_SPARSE_NORMALIZE };
822 auto& lookup_block = circuit_builder.blocks.lookup;
823 if (aes_plookup_tables.contains(table_id)) {
824 uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]);
825 uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]);
826 if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) {
827 bool find_out = find_position(real_out_idx);
828 auto q_c = lookup_block.q_c()[gate_index];
829 if (q_c.is_zero()) {
830 if (find_out) {
831 variables_in_one_gate.erase(real_out_idx);
832 }
833 }
834 }
835 }
836}
837
849template <typename FF, typename CircuitBuilder>
851 size_t gate_index)
852{
853 auto find_position = [&](uint32_t real_variable_index) {
854 return variables_in_one_gate.contains(real_variable_index);
855 };
856 auto& lookup_block = circuit_builder.blocks.lookup;
857 std::unordered_set<BasicTableId> sha256_plookup_tables{ BasicTableId::SHA256_WITNESS_SLICE_3,
858 BasicTableId::SHA256_WITNESS_SLICE_7_ROTATE_4,
859 BasicTableId::SHA256_WITNESS_SLICE_8_ROTATE_7,
860 BasicTableId::SHA256_WITNESS_SLICE_14_ROTATE_1,
861 BasicTableId::SHA256_BASE16,
862 BasicTableId::SHA256_BASE16_ROTATE2,
863 BasicTableId::SHA256_BASE28,
864 BasicTableId::SHA256_BASE28_ROTATE3,
865 BasicTableId::SHA256_BASE28_ROTATE6 };
866 if (sha256_plookup_tables.contains(table_id)) {
867 uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]);
868 uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]);
869 if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) {
870 // auto q_m = lookup_block.q_m()[gate_index];
871 auto q_c = lookup_block.q_c()[gate_index];
872 bool find_out = find_position(real_out_idx);
873 // bool find_right = find_position(real_right_idx);
874 if (q_c.is_zero()) {
875 if (find_out) {
876 variables_in_one_gate.erase(real_out_idx);
877 }
878 }
879 if (table_id == SHA256_BASE16_ROTATE2 || table_id == SHA256_BASE28_ROTATE6) {
880 // we want to remove false cases for special tables even though their selectors != 0
881 // because they are used in read_from_1_to_2_table function, and they aren't dangerous
882 variables_in_one_gate.erase(real_out_idx);
883 }
884 }
885 }
886}
887
897template <typename FF, typename CircuitBuilder>
899 size_t gate_index)
900{
901 auto find_position = [&](uint32_t real_variable_index) {
902 return variables_in_one_gate.contains(real_variable_index);
903 };
904
905 std::unordered_set<BasicTableId> keccak_plookup_tables{
906 BasicTableId::KECCAK_INPUT, BasicTableId::KECCAK_OUTPUT, BasicTableId::KECCAK_CHI, BasicTableId::KECCAK_THETA,
907 BasicTableId::KECCAK_RHO, BasicTableId::KECCAK_RHO_1, BasicTableId::KECCAK_RHO_2, BasicTableId::KECCAK_RHO_3,
908 BasicTableId::KECCAK_RHO_4, BasicTableId::KECCAK_RHO_5, BasicTableId::KECCAK_RHO_6, BasicTableId::KECCAK_RHO_7,
909 BasicTableId::KECCAK_RHO_8, BasicTableId::KECCAK_RHO_9
910 };
911
912 auto& lookup_block = circuit_builder.blocks.lookup;
913
914 if (keccak_plookup_tables.contains(table_id)) {
915 uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]);
916 uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]);
917 if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) {
918 bool find_out = find_position(real_out_idx);
919 auto q_c = lookup_block.q_c()[gate_index];
920 if (q_c.is_zero()) {
921 if (find_out) {
922 variables_in_one_gate.erase(real_out_idx);
923 }
924 }
925 }
926 }
927}
928
938template <typename FF, typename CircuitBuilder>
940{
941 auto find_position = [&](uint32_t real_variable_index) {
942 return variables_in_one_gate.contains(real_variable_index);
943 };
944 auto& lookup_block = circuit_builder.blocks.lookup;
945 auto& lookup_tables = circuit_builder.get_lookup_tables();
946 auto table_index = static_cast<size_t>(static_cast<uint256_t>(lookup_block.q_3()[gate_index]));
947 for (const auto& table : lookup_tables) {
948 if (table.table_index == table_index) {
949 std::unordered_set<bb::fr> column_1(table.column_1.begin(), table.column_1.end());
950 std::unordered_set<bb::fr> column_2(table.column_2.begin(), table.column_2.end());
951 std::unordered_set<bb::fr> column_3(table.column_3.begin(), table.column_3.end());
952 bb::plookup::BasicTableId table_id = table.id;
953 // false cases for AES
954 this->remove_unnecessary_aes_plookup_variables(table_id, gate_index);
955 // false cases for sha256
956 this->remove_unnecessary_sha256_plookup_variables(table_id, gate_index);
957 // false cases for keccak
958 this->remove_unnecessary_keccak_plookup_variables(table_id, gate_index);
959 // if the amount of unique elements from columns of plookup tables = 1, it means that
960 // variable from this column aren't used and we can remove it.
961 if (column_1.size() == 1) {
962 uint32_t left_idx = lookup_block.w_l()[gate_index];
963 uint32_t real_left_idx = this->to_real(left_idx);
964 bool find_left = find_position(real_left_idx);
965 if (find_left) {
966 variables_in_one_gate.erase(real_left_idx);
967 }
968 }
969 if (column_2.size() == 1) {
970 uint32_t real_right_idx = this->to_real(lookup_block.w_r()[gate_index]);
971 bool find_right = find_position(real_right_idx);
972 if (find_right) {
973 variables_in_one_gate.erase(real_right_idx);
974 }
975 }
976 if (column_3.size() == 1) {
977 uint32_t real_out_idx = this->to_real(lookup_block.w_o()[gate_index]);
978 bool find_out = find_position(real_out_idx);
979 if (find_out) {
980 variables_in_one_gate.erase(real_out_idx);
981 }
982 }
983 }
984 }
985}
986
993template <typename FF, typename CircuitBuilder>
995{
996 auto& lookup_block = circuit_builder.blocks.lookup;
997 if (lookup_block.size() > 0) {
998 for (size_t i = 0; i < lookup_block.size(); i++) {
999 this->process_current_plookup_gate(i);
1000 }
1001 }
1002}
1003
1012template <typename FF, typename CircuitBuilder>
1014{
1015 auto& memory_block = circuit_builder.blocks.memory;
1016 std::vector<uint32_t> to_remove;
1017 for (const auto& var_idx : variables_in_one_gate) {
1018 KeyPair key = { var_idx, &memory_block };
1019 if (auto search = variable_gates.find(key); search != variable_gates.end()) {
1020 std::vector<size_t> gate_indexes = variable_gates[key];
1021 BB_ASSERT_EQ(gate_indexes.size(), 1U);
1022 size_t gate_idx = gate_indexes[0];
1023 auto q_1 = memory_block.q_1()[gate_idx];
1024 auto q_2 = memory_block.q_2()[gate_idx];
1025 auto q_3 = memory_block.q_3()[gate_idx];
1026 auto q_4 = memory_block.q_4()[gate_idx];
1027 auto q_m = memory_block.q_m()[gate_idx];
1028 auto q_arith = read_gate_selector(memory_block, GateKind::Arith, gate_idx);
1029 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() &&
1030 q_arith.is_zero()) {
1031 // record witness can be in both ROM and RAM gates, so we can ignore q_c
1032 // record witness is written as 4th variable in RAM/ROM read/write gate, so we can get 4th
1033 // wire value and check it with our variable
1034 if (this->to_real(memory_block.w_4()[gate_idx]) == var_idx) {
1035 to_remove.emplace_back(var_idx);
1036 }
1037 }
1038 }
1039 }
1040 for (const auto& elem : to_remove) {
1041 variables_in_one_gate.erase(elem);
1042 }
1043}
1044
1052template <typename FF, typename CircuitBuilder>
1054{
1055 variables_in_one_gate.clear();
1056 for (const auto& pair : variables_gate_counts) {
1057 bool is_not_constant_variable = check_is_not_constant_variable(pair.first);
1058 if (pair.second == 1 && pair.first != 0 && is_not_constant_variable) {
1059 variables_in_one_gate.insert(pair.first);
1060 }
1061 }
1062 auto range_lists = circuit_builder.range_lists;
1063 std::unordered_set<uint32_t> decompose_variables;
1064 for (auto& pair : range_lists) {
1065 for (auto& elem : pair.second.variable_indices) {
1066 bool is_not_constant_variable = check_is_not_constant_variable(elem);
1067 if (variables_gate_counts[circuit_builder.real_variable_index[elem]] == 1 && is_not_constant_variable) {
1068 decompose_variables.insert(circuit_builder.real_variable_index[elem]);
1069 }
1070 }
1071 }
1072 remove_unnecessary_decompose_variables(decompose_variables);
1073 remove_unnecessary_plookup_variables();
1074 remove_unnecessary_range_constrains_variables();
1075
1076 // Remove variables that are intentionally in one gate (e.g., fix_witness, inverse checks).
1077 // These are marked at the source via update_used_witnesses().
1078 // AUDITTODO: used_witnesses stores raw witness indices, but variables_in_one_gate contains
1079 // real_variable_index values. If a witness is copy-constrained (aliased), its raw index may
1080 // differ from its real_variable_index, causing the erase to fail silently. Should convert:
1081 // variables_in_one_gate.erase(circuit_builder.real_variable_index[elem]);
1082 for (const auto& elem : circuit_builder.get_used_witnesses()) {
1083 variables_in_one_gate.erase(elem);
1084 }
1085 remove_record_witness_variables();
1086
1087 // Remove variables that only appear in sorted ROM gates - these are constrained via tau tags
1088 // (permutation argument) rather than copy constraints, matching how connected components
1089 // are filtered with is_process_rom_cc
1090 auto& memory_block = circuit_builder.blocks.memory;
1091 std::vector<uint32_t> to_remove;
1092 for (const auto& var_idx : variables_in_one_gate) {
1093 if (variable_only_in_sorted_rom_gates(var_idx, memory_block)) {
1094 to_remove.emplace_back(var_idx);
1095 }
1096 }
1097 for (const auto& elem : to_remove) {
1098 variables_in_one_gate.erase(elem);
1099 }
1100
1101 return variables_in_one_gate;
1102}
1103
1109template <typename FF, typename CircuitBuilder>
1111{
1112 info("╔═══════╦═══════╦═════════════╦═══════════╦══════════════╗");
1113 info("║ CC# ║ Size ║ Range List ║ Finalize ║ Process ROM ║");
1114 info("╠═══════╬═══════╬═════════════╬═══════════╬══════════════╣");
1115
1116 for (size_t i = 0; i < connected_components.size(); i++) {
1117 const auto& cc = connected_components[i];
1118 std::ostringstream line;
1119
1120 line << "║ " << std::setw(5) << std::right << (i + 1) << " ║ " << std::setw(5) << std::right << cc.size()
1121 << " ║ " << std::setw(11) << std::left << (cc.is_range_list_cc ? "Yes" : "No") << " ║ " << std::setw(9)
1122 << std::left << (cc.is_finalize_cc ? "Yes" : "No") << " ║ " << std::setw(12) << std::left
1123 << (cc.is_process_rom_cc ? "Yes" : "No") << " ║";
1124 info(line.str());
1125 }
1126 info("╚═══════╩═══════╩═════════════╩═══════════╩══════════════╝");
1127 info("Total connected components: ", connected_components.size());
1128}
1129
1136template <typename FF, typename CircuitBuilder> void StaticAnalyzer_<FF, CircuitBuilder>::print_variables_gate_counts()
1137{
1138 for (const auto& it : variables_gate_counts) {
1139 info("number of gates with variables ", it.first, " == ", it.second);
1140 }
1141}
1142
1150template <typename FF, typename CircuitBuilder>
1152{
1153 auto q_arith = read_gate_selector(block, GateKind::Arith, gate_index);
1154 if (!q_arith.is_zero()) {
1155 info("q_arith == ", q_arith);
1156 // fisrtly, print selectors for standard plonk gate
1157 info("q_m == ", block.q_m()[gate_index]);
1158 info("q1 == ", block.q_1()[gate_index]);
1159 info("q2 == ", block.q_2()[gate_index]);
1160 info("q3 == ", block.q_3()[gate_index]);
1161 info("q4 == ", block.q_4()[gate_index]);
1162 info("q_c == ", block.q_c()[gate_index]);
1163
1164 if (q_arith == FF(2)) {
1165 // we have to print w_4_shift from next gate
1166 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1167 }
1168 if (q_arith == FF(3)) {
1169 // we have to print w_4_shift and w_1_shift from the next gate
1170 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1171 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1172 }
1173 } else {
1174 return;
1175 }
1176}
1177
1185template <typename FF, typename CircuitBuilder>
1187{
1188 auto q_elliptic = read_gate_selector(block, GateKind::Elliptic, gate_index);
1189 if (!q_elliptic.is_zero()) {
1190 info("q_elliptic == ", q_elliptic);
1191 info("q_1 == ", block.q_1()[gate_index]);
1192 info("q_m == ", block.q_m()[gate_index]);
1193 bool is_elliptic_add_gate = !block.q_1()[gate_index].is_zero() && block.q_m()[gate_index].is_zero();
1194 bool is_elliptic_dbl_gate = block.q_1()[gate_index].is_zero() && block.q_m()[gate_index] == FF::one();
1195 if (is_elliptic_add_gate) {
1196 info("x2 == ", block.w_l()[gate_index + 1]);
1197 info("x3 == ", block.w_r()[gate_index + 1]);
1198 info("y3 == ", block.w_o()[gate_index + 1]);
1199 info("y2 == ", block.w_4()[gate_index + 1]);
1200 }
1201 if (is_elliptic_dbl_gate) {
1202 info("x3 == ", block.w_r()[gate_index + 1]);
1203 info("y3 == ", block.w_o()[gate_index + 1]);
1204 }
1205 } else {
1206 return;
1207 }
1208}
1209
1218template <typename FF, typename CircuitBuilder>
1220{
1221 auto q_lookup = read_gate_selector(block, GateKind::Lookup, gate_index);
1222 if (!q_lookup.is_zero()) {
1223 info("q_lookup == ", q_lookup);
1224 auto q_2 = block.q_2()[gate_index];
1225 auto q_m = block.q_m()[gate_index];
1226 auto q_c = block.q_c()[gate_index];
1227 info("q_2 == ", q_2);
1228 info("q_m == ", q_m);
1229 info("q_c == ", q_c);
1230 if (!q_2.is_zero()) {
1231 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1232 }
1233 if (!q_m.is_zero()) {
1234 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1235 }
1236 if (!q_c.is_zero()) {
1237 info("w_3_shift == ", block.w_o()[gate_index + 1]);
1238 }
1239 } else {
1240 return;
1241 }
1242}
1243
1252template <typename FF, typename CircuitBuilder>
1254{
1255 auto q_delta_range = read_gate_selector(block, GateKind::DeltaRange, gate_index);
1256 if (!q_delta_range.is_zero()) {
1257 info("q_delta_range == ", q_delta_range);
1258 info("w_1 == ", block.w_l()[gate_index]);
1259 info("w_2 == ", block.w_r()[gate_index]);
1260 info("w_3 == ", block.w_o()[gate_index]);
1261 info("w_4 == ", block.w_4()[gate_index]);
1262 info("w_1_shift == ", block.w_l()[gate_index]);
1263 } else {
1264 return;
1265 }
1266}
1267
1276template <typename FF, typename CircuitBuilder>
1278{
1279 auto external_selector = read_gate_selector(block, GateKind::Poseidon2Ext, gate_index);
1280 bool nonzero = !external_selector.is_zero();
1281 if constexpr (IsMegaBuilder<CircuitBuilder>) {
1282 nonzero = nonzero || !read_gate_selector(block, GateKind::Poseidon2ExtInitial, gate_index).is_zero() ||
1283 !read_gate_selector(block, GateKind::Poseidon2QuadInt, gate_index).is_zero();
1284 } else {
1285 nonzero = nonzero || !read_gate_selector(block, GateKind::Poseidon2Int, gate_index).is_zero();
1286 }
1287 if (nonzero) {
1288 info("q_poseidon2_external == ", external_selector);
1289 if constexpr (IsMegaBuilder<CircuitBuilder>) {
1290 info("q_poseidon2_external_initial == ",
1291 read_gate_selector(block, GateKind::Poseidon2ExtInitial, gate_index));
1292 info("q_poseidon2_quad_internal == ", read_gate_selector(block, GateKind::Poseidon2QuadInt, gate_index));
1293 } else {
1294 info("q_poseidon2_internal == ", read_gate_selector(block, GateKind::Poseidon2Int, gate_index));
1295 }
1296 info("w_1 == ", block.w_l()[gate_index]);
1297 info("w_2 == ", block.w_r()[gate_index]);
1298 info("w_3 == ", block.w_o()[gate_index]);
1299 info("w_4 == ", block.w_4()[gate_index]);
1300 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1301 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1302 info("w_3_shift == ", block.w_o()[gate_index + 1]);
1303 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1304 } else {
1305 return;
1306 }
1307}
1308
1317template <typename FF, typename CircuitBuilder>
1319{
1320 auto q_nnf = read_gate_selector(block, GateKind::Nnf, gate_idx);
1321 if (!q_nnf.is_zero()) {
1322 info("q_nnf == ", q_nnf);
1323 auto q_2 = block.q_2()[gate_idx];
1324 auto q_3 = block.q_3()[gate_idx];
1325 auto q_4 = block.q_4()[gate_idx];
1326 auto q_m = block.q_m()[gate_idx];
1327 if (q_3 == FF::one() && q_4 == FF::one()) {
1328 info("w_1_shift == ", block.w_l()[gate_idx + 1]);
1329 info("w_2_shift == ", block.w_r()[gate_idx + 1]);
1330
1331 } else if (q_3 == FF::one() && q_m == FF::one()) {
1332 info("w_1_shift == ", block.w_l()[gate_idx + 1]);
1333 info("w_2_shift == ", block.w_r()[gate_idx + 1]);
1334 info("w_3_shift == ", block.w_o()[gate_idx + 1]);
1335 info("w_4_shift == ", block.w_4()[gate_idx + 1]);
1336 } else if (q_2 == FF::one() && (q_3 == FF::one() || q_4 == FF::one() || q_m == FF::one())) {
1337 info("w_1_shift == ", block.w_l()[gate_idx + 1]);
1338 info("w_2_shift == ", block.w_r()[gate_idx + 1]);
1339 if (q_4 == FF::one() || q_m == FF::one()) {
1340 info("w_3_shift == ", block.w_o()[gate_idx + 1]);
1341 info("w_4_shift == ", block.w_4()[gate_idx + 1]);
1342 }
1343 }
1344 } else {
1345 return;
1346 }
1347}
1348
1357template <typename FF, typename CircuitBuilder>
1359{
1360 auto q_memory = read_gate_selector(block, GateKind::Memory, gate_index);
1361 if (!q_memory.is_zero()) {
1362 info("q_memory == ", q_memory);
1363 auto q_1 = block.q_1()[gate_index];
1364 auto q_2 = block.q_2()[gate_index];
1365 auto q_3 = block.q_3()[gate_index];
1366 auto q_4 = block.q_4()[gate_index];
1367 if (q_1 == FF::one() && q_4 == FF::one()) {
1368 info("q_1 == ", q_1);
1369 info("q_4 == ", q_4);
1370 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1371 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1372 } else if (q_1 == FF::one() && q_2 == FF::one()) {
1373 info("q_1 == ", q_1);
1374 info("q_2 == ", q_2);
1375 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1376 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1377 } else if (!q_3.is_zero()) {
1378 info("q_3 == ", q_3);
1379 info("w_1_shift == ", block.w_l()[gate_index + 1]);
1380 info("w_2_shift == ", block.w_r()[gate_index + 1]);
1381 info("w_3_shift == ", block.w_o()[gate_index + 1]);
1382 info("w_4_shift == ", block.w_4()[gate_index + 1]);
1383 }
1384 } else {
1385 return;
1386 }
1387}
1388
1396template <typename FF, typename CircuitBuilder>
1398{
1400 for (const auto& [key, gates] : variable_gates) {
1401 if (key.first == real_idx) {
1402 for (size_t i = 0; i < gates.size(); i++) {
1403 size_t gate_index = gates[i];
1404 // key.second is a pointer to the block
1405 auto& block = *const_cast<BlockType*>(static_cast<const BlockType*>(key.second));
1406 info("---- printing variables in this gate");
1407 info("w_l == ",
1408 block.w_l()[gate_index],
1409 " w_r == ",
1410 block.w_r()[gate_index],
1411 " w_o == ",
1412 block.w_o()[gate_index],
1413 " w_4 == ",
1414 block.w_4()[gate_index]);
1415 info("---- printing gate info where variable with index ", key.first, " was found ----");
1416 print_arithmetic_gate_info(gate_index, block);
1417 print_elliptic_gate_info(gate_index, block);
1418 print_plookup_gate_info(gate_index, block);
1419 print_poseidon2s_gate_info(gate_index, block);
1420 print_delta_range_gate_info(gate_index, block);
1421 print_nnf_gate_info(gate_index, block);
1422 print_memory_gate_info(gate_index, block);
1423 if constexpr (IsMegaBuilder<CircuitBuilder>) {
1424 auto q_databus = read_gate_selector(block, GateKind::BusRead, gate_index);
1425 if (!q_databus.is_zero()) {
1426 info("q_databus == ", q_databus);
1427 }
1428 }
1429 info("---- finished printing ----");
1430 }
1431 }
1432 }
1433}
1434
1444template <typename FF, typename CircuitBuilder>
1446 analyze_circuit(bool filter_cc)
1447{
1448 auto variables_in_one_gate = get_variables_in_one_gate();
1449 find_connected_components();
1450 if (filter_cc) {
1451 std::vector<ConnectedComponent> main_connected_components;
1452 main_connected_components.reserve(connected_components.size());
1453 for (auto& cc : connected_components) {
1454 if (!cc.is_range_list_cc && !cc.is_finalize_cc && !cc.is_process_rom_cc) {
1455 main_connected_components.emplace_back(cc);
1456 }
1457 }
1458 return std::make_pair(std::move(main_connected_components), std::move(variables_in_one_gate));
1459 }
1460 return std::make_pair(connected_components, std::move(variables_in_one_gate));
1461}
1462
1465
1466} // namespace cdg
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
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:1253
void process_execution_trace()
Definition graph.cpp:258
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:1358
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:1219
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:163
std::unordered_map< uint32_t, std::vector< uint32_t > > variable_adjacency_lists
Definition graph.hpp:172
void remove_unnecessary_decompose_variables(const std::unordered_set< uint32_t > &decompose_variables)
this method removes unnecessary variables from decompose chains
Definition graph.cpp:724
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:523
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:494
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:423
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:850
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:1053
void remove_record_witness_variables()
this method removes record witness variables from variables in one gate. initially record witness is ...
Definition graph.cpp:1013
void print_variable_info(const uint32_t real_idx)
this method prints all information about gates where variable was found
Definition graph.cpp:1397
void remove_unnecessary_range_constrains_variables()
this method removes variables from range constraints that are not security critical
Definition graph.cpp:774
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:1446
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:1186
StaticAnalyzer_()=default
void process_gate_variables(std::vector< uint32_t > &gate_variables, size_t gate_index, auto &blk)
this method processes variables from a gate by removing duplicates and updating tracking structures
Definition graph.cpp:38
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:440
bool is_gate_sorted_rom(auto &memory_block, size_t gate_idx) const
this method checks if current gate is sorted ROM gate
Definition graph.cpp:555
void print_connected_components_info()
this method prints additional information about connected components that were found in the graph
Definition graph.cpp:1110
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:97
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:1277
std::unordered_map< uint32_t, size_t > variables_gate_counts
Definition graph.hpp:175
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:406
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:812
CircuitBuilder & circuit_builder
Definition graph.hpp:168
void remove_unnecessary_plookup_variables()
this method removes false cases plookup variables from variables in one gate
Definition graph.cpp:994
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:1318
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:1151
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:939
std::vector< uint32_t > extract_gate_variables(size_t index, Block &blk, const bb::gate_patterns::GatePattern &pattern, bb::GateKind kind)
Extract gate variables using a declarative pattern.
Definition graph.cpp:70
std::vector< uint32_t > get_eccop_part_connected_component(size_t index, auto &blk)
this method creates connected components from elliptic curve operation gates
Definition graph.cpp:218
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:615
void print_variables_gate_counts()
this method prints a number of gates for each variable
Definition graph.cpp:1136
void mark_process_rom_connected_component()
this method marks some connected components if they were created by function process_rom_array....
Definition graph.cpp:593
std::unordered_map< uint32_t, size_t > variables_degree
Definition graph.hpp:177
void remove_unnecessary_keccak_plookup_variables(bb::plookup::BasicTableId &table_id, size_t gate_index)
This method removes false positive cases from keccak lookup tables. Tables which are enumerated in ke...
Definition graph.cpp:898
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:671
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:475
void mark_finalize_connected_components()
this method marks some connected components like they represent separated finalize blocks the point i...
Definition graph.cpp:643
bool variable_only_in_sorted_rom_gates(uint32_t var_idx, auto &blk) const
this method checks that every gate for given variable in a given block is sorted ROM gate
Definition graph.cpp:570
#define info(...)
Definition log.hpp:93
@ SHA256_BASE16_ROTATE2
Definition types.hpp:50
@ SHA256_BASE28_ROTATE6
Definition types.hpp:47
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
ExecutionTraceBlock< fr, 4 > MegaTraceBlock
FF read_gate_selector(const ExecutionTraceBlock< FF, NUM_WIRES > &block, GateKind kind, size_t idx)
Gate-selector value at (block, idx) for kind, returning zero if the block does not own this kind or t...
GateKind
Tag identifying which gate selector a block owns. Used by cross-block readers to decide whether (bloc...
Definition graph.cpp:21
std::pair< uint32_t, const void * > KeyPair
Definition graph.hpp:33
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
Pattern defining which wires are constrained by a gate type.
Selector values read from a gate.
void throw_or_abort(std::string const &err)
VectorField result