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"
4#include <algorithm>
5#include <array>
6#include <stack>
7
8using namespace bb::plookup;
9using namespace bb;
10
11namespace cdg {
12
20template <typename FF>
22{
23 auto blocks_data = ultra_builder.blocks.get();
24 size_t index = 0;
25 for (size_t i = 0; i < blocks_data.size(); i++) {
26 if ((void*)(&blocks_data[i]) == (void*)(&block)) {
27 index = i;
28 break;
29 }
30 }
31 return index;
32}
33
48template <typename FF>
49inline void StaticAnalyzer_<FF>::process_gate_variables(std::vector<uint32_t>& gate_variables,
50 size_t gate_index,
51 size_t block_idx)
52{
53 auto unique_variables = std::unique(gate_variables.begin(), gate_variables.end());
54 gate_variables.erase(unique_variables, gate_variables.end());
55 if (gate_variables.empty()) {
56 return;
57 }
58 for (auto& var_idx : gate_variables) {
59 KeyPair key = std::make_pair(var_idx, block_idx);
60 variable_gates[key].emplace_back(gate_index);
61 }
62 for (const auto& variable_index : gate_variables) {
63 variables_gate_counts[variable_index] += 1;
64 }
65}
66
78template <typename FF>
80 bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock& blk)
81{
82 auto q_arith = blk.q_arith()[index];
83 std::vector<uint32_t> gate_variables;
84 std::vector<uint32_t> minigate_variables;
85 std::vector<std::vector<uint32_t>> all_gates_variables;
86 if (q_arith.is_zero()) {
87 return {};
88 }
89 auto q_m = blk.q_m()[index];
90 auto q_1 = blk.q_1()[index];
91 auto q_2 = blk.q_2()[index];
92 auto q_3 = blk.q_3()[index];
93 auto q_4 = blk.q_4()[index];
94
95 uint32_t left_idx = blk.w_l()[index];
96 uint32_t right_idx = blk.w_r()[index];
97 uint32_t out_idx = blk.w_o()[index];
98 uint32_t fourth_idx = blk.w_4()[index];
99 if (q_m.is_zero() && q_1 == 1 && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() && q_arith == FF::one()) {
100 // this is fixed_witness gate. So, variable index contains in left wire. So, we have to take only it.
101 fixed_variables.insert(this->to_real(ultra_circuit_builder, left_idx));
102 } else if (!q_m.is_zero() || q_1 != FF::one() || !q_2.is_zero() || !q_3.is_zero() || !q_4.is_zero()) {
103 // this is not the gate for fix_witness, so we have to process this gate
104 if (!q_m.is_zero()) {
105 gate_variables.emplace_back(left_idx);
106 gate_variables.emplace_back(right_idx);
107 } else {
108 if (!q_1.is_zero()) {
109 gate_variables.emplace_back(left_idx);
110 }
111 if (!q_2.is_zero()) {
112 gate_variables.emplace_back(right_idx);
113 }
114 }
115
116 if (!q_3.is_zero()) {
117 gate_variables.emplace_back(out_idx);
118 }
119 if (!q_4.is_zero()) {
120 gate_variables.emplace_back(fourth_idx);
121 }
122 if (q_arith == FF(2)) {
123 // We have to use w_4_shift from the next gate
124 // if and only if the current gate isn't last, cause we can't
125 // look into the next gate
126 if (index != blk.size() - 1) {
127 gate_variables.emplace_back(blk.w_4()[index + 1]);
128 }
129 }
130 if (q_arith == FF(3)) {
131 // In this gate mini gate is enabled, we have 2 equations:
132 // q_1 * w_1 + q_2 * w_2 + q_3 * w_3 + q_4 * w_4 + q_c + 2 * w_4_omega = 0
133 // w_1 + w_4 - w_1_omega + q_m = 0
134 minigate_variables.emplace_back(left_idx);
135 minigate_variables.emplace_back(fourth_idx);
136 if (index != blk.size() - 1) {
137 gate_variables.emplace_back(blk.w_4()[index + 1]);
138 minigate_variables.emplace_back(blk.w_l()[index + 1]);
139 }
140 }
141 }
142 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
143 minigate_variables = this->to_real(ultra_circuit_builder, minigate_variables);
144 this->process_gate_variables(gate_variables, index, block_idx);
145 this->process_gate_variables(minigate_variables, index, block_idx);
146 all_gates_variables.emplace_back(gate_variables);
147 if (!minigate_variables.empty()) {
148 all_gates_variables.emplace_back(minigate_variables);
149 }
150
151 return all_gates_variables;
152}
153
165template <typename FF>
167 bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock& blk)
168{
169 std::vector<uint32_t> gate_variables;
170 if (!blk.q_elliptic()[index].is_zero()) {
171 gate_variables.reserve(6);
172 bool is_elliptic_add_gate = !blk.q_1()[index].is_zero() && blk.q_m()[index].is_zero();
173 bool is_elliptic_dbl_gate = blk.q_1()[index].is_zero() && blk.q_m()[index] == FF::one();
174 auto right_idx = blk.w_r()[index];
175 auto out_idx = blk.w_o()[index];
176 gate_variables.emplace_back(right_idx);
177 gate_variables.emplace_back(out_idx);
178 if (index != blk.size() - 1) {
179 if (is_elliptic_add_gate) {
180 // if this gate is ecc_add_gate, we have to get indices x2, x3, y3, y2 from the next gate
181 gate_variables.emplace_back(blk.w_l()[index + 1]);
182 gate_variables.emplace_back(blk.w_r()[index + 1]);
183 gate_variables.emplace_back(blk.w_o()[index + 1]);
184 gate_variables.emplace_back(blk.w_4()[index + 1]);
185 }
186 if (is_elliptic_dbl_gate) {
187 // if this gate is ecc_dbl_gate, we have to indices x3, y3 from right and output wires
188 gate_variables.emplace_back(blk.w_r()[index + 1]);
189 gate_variables.emplace_back(blk.w_o()[index + 1]);
190 }
191 }
192 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
193 this->process_gate_variables(gate_variables, index, block_idx);
194 }
195 return gate_variables;
196}
197
209template <typename FF>
211 bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index, size_t blk_idx, UltraBlock& block)
212{
213 std::vector<uint32_t> gate_variables = {};
214 if (!block.q_delta_range()[index].is_zero()) {
215 gate_variables.reserve(4);
216 gate_variables.emplace_back(block.w_l()[index]);
217 gate_variables.emplace_back(block.w_r()[index]);
218 gate_variables.emplace_back(block.w_o()[index]);
219 gate_variables.emplace_back(block.w_4()[index]);
220 }
221 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
222 this->process_gate_variables(gate_variables, index, blk_idx);
223 return gate_variables;
224}
225
237template <typename FF>
239 bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index, size_t blk_idx, UltraBlock& block)
240{
241 std::vector<uint32_t> gate_variables;
242 auto q_lookup_type = block.q_lookup_type()[index];
243 if (!q_lookup_type.is_zero()) {
244 gate_variables.reserve(6);
245 auto q_2 = block.q_2()[index];
246 auto q_m = block.q_m()[index];
247 auto q_c = block.q_c()[index];
248 gate_variables.emplace_back(block.w_l()[index]);
249 gate_variables.emplace_back(block.w_r()[index]);
250 gate_variables.emplace_back(block.w_o()[index]);
251 if (index < block.size() - 1) {
252 if (!q_2.is_zero()) {
253 gate_variables.emplace_back(block.w_l()[index + 1]);
254 }
255 if (!q_m.is_zero()) {
256 gate_variables.emplace_back(block.w_r()[index + 1]);
257 }
258 if (!q_c.is_zero()) {
259 gate_variables.emplace_back(block.w_o()[index + 1]);
260 }
261 }
262 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
263 this->process_gate_variables(gate_variables, index, blk_idx);
264 }
265 return gate_variables;
266}
267
277template <typename FF>
279 bb::UltraCircuitBuilder& ultra_circuit_builder, size_t index, size_t blk_idx, UltraBlock& block)
280{
281 std::vector<uint32_t> gate_variables;
282 auto internal_selector = block.q_poseidon2_internal()[index];
283 auto external_selector = block.q_poseidon2_external()[index];
284 if (!internal_selector.is_zero() || !external_selector.is_zero()) {
285 gate_variables.reserve(8);
286 gate_variables.emplace_back(block.w_l()[index]);
287 gate_variables.emplace_back(block.w_r()[index]);
288 gate_variables.emplace_back(block.w_o()[index]);
289 gate_variables.emplace_back(block.w_4()[index]);
290 if (index != block.size() - 1) {
291 gate_variables.emplace_back(block.w_l()[index + 1]);
292 gate_variables.emplace_back(block.w_r()[index + 1]);
293 gate_variables.emplace_back(block.w_o()[index + 1]);
294 gate_variables.emplace_back(block.w_4()[index + 1]);
295 }
296 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
297 this->process_gate_variables(gate_variables, index, blk_idx);
298 }
299 return gate_variables;
300}
301
311template <typename FF>
313 bb::UltraCircuitBuilder& ultra_builder, size_t index, size_t blk_idx, UltraBlock& block)
314{
315 std::vector<uint32_t> gate_variables;
316 if (!block.q_memory()[index].is_zero()) {
317 gate_variables.reserve(8);
318 auto q_1 = block.q_1()[index];
319 auto q_2 = block.q_2()[index];
320 auto q_3 = block.q_3()[index];
321 auto q_4 = block.q_4()[index];
322 [[maybe_unused]] auto q_m = block.q_m()[index];
323 [[maybe_unused]] auto q_c = block.q_c()[index];
324
325 [[maybe_unused]] auto w_l = block.w_l()[index];
326 [[maybe_unused]] auto w_r = block.w_r()[index];
327 [[maybe_unused]] auto w_o = block.w_o()[index];
328 [[maybe_unused]] auto w_4 = block.w_4()[index];
329
330 if (q_1 == FF::one() && q_4 == FF::one()) {
331 ASSERT(q_3.is_zero());
332 // ram timestamp check
333 if (index < block.size() - 1) {
334 gate_variables.insert(gate_variables.end(),
335 { block.w_r()[index + 1],
336 block.w_r()[index],
337 block.w_l()[index],
338 block.w_l()[index + 1],
339 block.w_o()[index] });
340 }
341 } else if (q_1 == FF::one() && q_2 == FF::one()) {
342 ASSERT(q_3.is_zero());
343 // rom constitency check
344 if (index < block.size() - 1) {
345 gate_variables.insert(
346 gate_variables.end(),
347 { block.w_l()[index], block.w_l()[index + 1], block.w_4()[index], block.w_4()[index + 1] });
348 }
349 } else {
350 // ram constitency check
351 if (!q_3.is_zero()) {
352 if (index < block.size() - 1) {
353 gate_variables.insert(gate_variables.end(),
354 { block.w_o()[index],
355 block.w_4()[index],
356 block.w_l()[index + 1],
357 block.w_r()[index + 1],
358 block.w_o()[index + 1],
359 block.w_4()[index + 1] });
360 }
361 }
362 }
363 }
364 gate_variables = this->to_real(ultra_builder, gate_variables);
365 this->process_gate_variables(gate_variables, index, blk_idx);
366 return gate_variables;
367}
368
378template <typename FF>
380 bb::UltraCircuitBuilder& ultra_builder, size_t index, size_t blk_idx, UltraBlock& block)
381{
382 std::vector<uint32_t> gate_variables;
383 if (!block.q_nnf()[index].is_zero()) {
384 gate_variables.reserve(8);
385 [[maybe_unused]] auto q_1 = block.q_1()[index];
386 auto q_2 = block.q_2()[index];
387 auto q_3 = block.q_3()[index];
388 auto q_4 = block.q_4()[index];
389 auto q_m = block.q_m()[index];
390 [[maybe_unused]] auto q_c = block.q_c()[index];
391
392 auto w_l = block.w_l()[index];
393 auto w_r = block.w_r()[index];
394 auto w_o = block.w_o()[index];
395 auto w_4 = block.w_4()[index];
396 if (q_3 == FF::one() && q_4 == FF::one()) {
397 // bigfield limb accumulation 1
398 if (index < block.size() - 1) {
399 gate_variables.insert(gate_variables.end(),
400 { w_l, w_r, w_o, w_4, block.w_l()[index + 1], block.w_r()[index + 1] }); // 6
401 }
402 } else if (q_3 == FF::one() && q_m == FF::one()) {
403 // bigfield limb accumulation 2
404 if (index < block.size() - 1) {
405 gate_variables.insert(gate_variables.end(),
406 { w_o,
407 w_4,
408 block.w_l()[index + 1],
409 block.w_r()[index + 1],
410 block.w_o()[index + 1],
411 block.w_4()[index + 1] });
412 }
413 } else if (q_2 == FF::one() && (q_3 == FF::one() || q_4 == FF::one() || q_m == FF::one())) {
414 // bigfield product cases
415 if (index < block.size() - 1) {
416 std::vector<uint32_t> limb_subproduct_vars = {
417 w_l, w_r, block.w_l()[index + 1], block.w_r()[index + 1]
418 };
419 if (q_3 == FF::one()) {
420 // bigfield product 1
421 ASSERT(q_4.is_zero() && q_m.is_zero());
422 gate_variables.insert(
423 gate_variables.end(), limb_subproduct_vars.begin(), limb_subproduct_vars.end());
424 gate_variables.insert(gate_variables.end(), { w_o, w_4 });
425 }
426 if (q_4 == FF::one()) {
427 // bigfield product 2
428 ASSERT(q_3.is_zero() && q_m.is_zero());
429 std::vector<uint32_t> non_native_field_gate_2 = { w_l, w_4, w_r, w_o, block.w_o()[index + 1] };
430 gate_variables.insert(
431 gate_variables.end(), non_native_field_gate_2.begin(), non_native_field_gate_2.end());
432 gate_variables.emplace_back(block.w_4()[index + 1]);
433 gate_variables.insert(
434 gate_variables.end(), limb_subproduct_vars.begin(), limb_subproduct_vars.end());
435 }
436 if (q_m == FF::one()) {
437 // bigfield product 3
438 ASSERT(q_4.is_zero() && q_3.is_zero());
439 gate_variables.insert(
440 gate_variables.end(), limb_subproduct_vars.begin(), limb_subproduct_vars.end());
441 gate_variables.insert(gate_variables.end(),
442 { w_4, block.w_o()[index + 1], block.w_4()[index + 1] });
443 }
444 }
445 }
446 }
447 gate_variables = this->to_real(ultra_builder, gate_variables);
448 this->process_gate_variables(gate_variables, index, blk_idx);
449 return gate_variables;
450}
451
459template <typename FF>
461 bb::UltraCircuitBuilder& ultra_builder, const bb::RomTranscript& rom_array)
462{
463 size_t block_index = find_block_index(ultra_builder, ultra_builder.blocks.memory);
464 BB_ASSERT_EQ(block_index, 5U);
465
466 // Every RomTranscript data structure has 2 main components that are interested for static analyzer:
467 // 1) records contains values that were put in the gate, we can use them to create connections between variables
468 // 2) states contains values witness indexes that we can find in the ROM record in the RomTrascript, so we can
469 // ignore state of the ROM transcript, because we still can connect all variables using variables from records.
470 std::vector<uint32_t> rom_table_variables;
471
472 for (const auto& record : rom_array.records) {
473 std::vector<uint32_t> gate_variables;
474 size_t gate_index = record.gate_index;
475
476 auto q_1 = ultra_builder.blocks.memory.q_1()[gate_index];
477 auto q_2 = ultra_builder.blocks.memory.q_2()[gate_index];
478 auto q_3 = ultra_builder.blocks.memory.q_3()[gate_index];
479 auto q_4 = ultra_builder.blocks.memory.q_4()[gate_index];
480 auto q_m = ultra_builder.blocks.memory.q_m()[gate_index];
481 auto q_c = ultra_builder.blocks.memory.q_c()[gate_index];
482
483 auto index_witness = record.index_witness;
484 auto vc1_witness = record.value_column1_witness; // state[0] from RomTranscript
485 auto vc2_witness = record.value_column2_witness; // state[1] from RomTranscript
486 auto record_witness = record.record_witness;
487
488 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()) {
489 // By default ROM read gate uses variables (w_1, w_2, w_3, w_4) = (index_witness, vc1_witness, vc2_witness,
490 // record_witness) So we can update all of them
491 gate_variables.emplace_back(index_witness);
492 if (vc1_witness != ultra_builder.zero_idx) {
493 gate_variables.emplace_back(vc1_witness);
494 }
495 if (vc2_witness != ultra_builder.zero_idx) {
496 gate_variables.emplace_back(vc2_witness);
497 }
498 gate_variables.emplace_back(record_witness);
499 }
500 gate_variables = this->to_real(ultra_builder, gate_variables);
501 this->process_gate_variables(gate_variables, gate_index, block_index);
502 // after process_gate_variables function gate_variables constists of real variables indexes, so we can add all
503 // this variables in the final vector to connect all of them
504 if (!gate_variables.empty()) {
505 rom_table_variables.insert(rom_table_variables.end(), gate_variables.begin(), gate_variables.end());
506 }
507 }
508 return rom_table_variables;
509}
510
518template <typename FF>
520 bb::UltraCircuitBuilder& ultra_builder, const bb::RamTranscript& ram_array)
521{
522 size_t block_index = find_block_index(ultra_builder, ultra_builder.blocks.memory);
523 BB_ASSERT_EQ(block_index, 5U);
524 std::vector<uint32_t> ram_table_variables;
525 for (const auto& record : ram_array.records) {
526 std::vector<uint32_t> gate_variables;
527 size_t gate_index = record.gate_index;
528
529 auto q_1 = ultra_builder.blocks.memory.q_1()[gate_index];
530 auto q_2 = ultra_builder.blocks.memory.q_2()[gate_index];
531 auto q_3 = ultra_builder.blocks.memory.q_3()[gate_index];
532 auto q_4 = ultra_builder.blocks.memory.q_4()[gate_index];
533 auto q_m = ultra_builder.blocks.memory.q_m()[gate_index];
534 auto q_c = ultra_builder.blocks.memory.q_c()[gate_index];
535
536 auto index_witness = record.index_witness;
537 auto timestamp_witness = record.timestamp_witness;
538 auto value_witness = record.value_witness;
539 auto record_witness = record.record_witness;
540
541 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() &&
542 (q_c.is_zero() || q_c == FF::one())) {
543 // By default RAM read/write gate uses variables (w_1, w_2, w_3, w_4) = (index_witness, timestamp_witness,
544 // value_witness, record_witness) So we can update all of them
545 gate_variables.emplace_back(index_witness);
546 if (timestamp_witness != ultra_builder.zero_idx) {
547 gate_variables.emplace_back(timestamp_witness);
548 }
549 if (value_witness != ultra_builder.zero_idx) {
550 gate_variables.emplace_back(value_witness);
551 }
552 gate_variables.emplace_back(record_witness);
553 }
554 gate_variables = this->to_real(ultra_builder, gate_variables);
555 this->process_gate_variables(gate_variables, gate_index, block_index);
556 // after process_gate_variables function gate_variables constists of real variables indexes, so we can add all
557 // these variables in the final vector to connect all of them
558 ram_table_variables.insert(ram_table_variables.end(), gate_variables.begin(), gate_variables.end());
559 }
560 return ram_table_variables;
561}
562
583template <typename FF>
584StaticAnalyzer_<FF>::StaticAnalyzer_(bb::UltraCircuitBuilder& ultra_circuit_constructor, bool connect_variables)
585{
586 this->variables_gate_counts =
587 std::unordered_map<uint32_t, size_t>(ultra_circuit_constructor.real_variable_index.size());
588 this->variable_adjacency_lists =
590 this->variables_degree = std::unordered_map<uint32_t, size_t>(ultra_circuit_constructor.real_variable_index.size());
591 for (const auto& variable_index : ultra_circuit_constructor.real_variable_index) {
592 variables_gate_counts[variable_index] = 0;
593 variables_degree[variable_index] = 0;
594 variable_adjacency_lists[variable_index] = {};
595 }
596
597 std::map<FF, uint32_t> constant_variable_indices = ultra_circuit_constructor.constant_variable_indices;
598 auto block_data = ultra_circuit_constructor.blocks.get();
599 for (size_t blk_idx = 1; blk_idx < block_data.size() - 1; blk_idx++) {
600 if (block_data[blk_idx].size() == 0) {
601 continue;
602 }
603 std::vector<uint32_t> sorted_variables;
604 for (size_t gate_idx = 0; gate_idx < block_data[blk_idx].size(); gate_idx++) {
605 auto arithmetic_gates_variables = get_arithmetic_gate_connected_component(
606 ultra_circuit_constructor, gate_idx, blk_idx, block_data[blk_idx]);
607 if (!arithmetic_gates_variables.empty() && connect_variables) {
608 for (const auto& gate_variables : arithmetic_gates_variables) {
609 connect_all_variables_in_vector(ultra_circuit_constructor, gate_variables);
610 }
611 }
612 auto elliptic_gate_variables = get_elliptic_gate_connected_component(
613 ultra_circuit_constructor, gate_idx, blk_idx, block_data[blk_idx]);
614 if (connect_variables) {
615 connect_all_variables_in_vector(ultra_circuit_constructor, elliptic_gate_variables);
616 }
617 auto lookup_gate_variables =
618 get_plookup_gate_connected_component(ultra_circuit_constructor, gate_idx, blk_idx, block_data[blk_idx]);
619 if (connect_variables) {
620 connect_all_variables_in_vector(ultra_circuit_constructor, lookup_gate_variables);
621 }
622 auto poseidon2_gate_variables = get_poseido2s_gate_connected_component(
623 ultra_circuit_constructor, gate_idx, blk_idx, block_data[blk_idx]);
624 if (connect_variables) {
625 connect_all_variables_in_vector(ultra_circuit_constructor, poseidon2_gate_variables);
626 }
627 auto memory_gate_variables =
628 get_memory_gate_connected_component(ultra_circuit_constructor, gate_idx, blk_idx, block_data[blk_idx]);
629 if (connect_variables) {
630 connect_all_variables_in_vector(ultra_circuit_constructor, memory_gate_variables);
631 }
632 auto nnf_gate_variables = get_non_native_field_gate_connected_component(
633 ultra_circuit_constructor, gate_idx, blk_idx, block_data[blk_idx]);
634 if (connect_variables) {
635 connect_all_variables_in_vector(ultra_circuit_constructor, nnf_gate_variables);
636 }
637 if (arithmetic_gates_variables.empty() && elliptic_gate_variables.empty() &&
638 lookup_gate_variables.empty() && poseidon2_gate_variables.empty() && memory_gate_variables.empty() &&
639 nnf_gate_variables.empty()) {
640 // if all vectors are empty it means that current block is delta range, and it needs another
641 // processing method
642 auto delta_range_gate_variables = get_sort_constraint_connected_component(
643 ultra_circuit_constructor, gate_idx, blk_idx, block_data[blk_idx]);
644 if (delta_range_gate_variables.empty()) {
645 if (connect_variables) {
646 connect_all_variables_in_vector(ultra_circuit_constructor, sorted_variables);
647 }
648 sorted_variables.clear();
649 } else {
650 sorted_variables.insert(
651 sorted_variables.end(), delta_range_gate_variables.begin(), delta_range_gate_variables.end());
652 }
653 }
654 }
655 }
656
657 const auto& rom_arrays = ultra_circuit_constructor.rom_ram_logic.rom_arrays;
658 if (!rom_arrays.empty()) {
659 for (const auto& rom_array : rom_arrays) {
660 std::vector<uint32_t> variable_indices =
661 this->get_rom_table_connected_component(ultra_circuit_constructor, rom_array);
662 if (connect_variables) {
663 this->connect_all_variables_in_vector(ultra_circuit_constructor, variable_indices);
664 }
665 }
666 }
667
668 const auto& ram_arrays = ultra_circuit_constructor.rom_ram_logic.ram_arrays;
669 if (!ram_arrays.empty()) {
670 for (const auto& ram_array : ram_arrays) {
671 std::vector<uint32_t> variable_indices =
672 this->get_ram_table_connected_component(ultra_circuit_constructor, ram_array);
673 if (connect_variables) {
674 this->connect_all_variables_in_vector(ultra_circuit_constructor, variable_indices);
675 }
676 }
677 }
678}
679
689template <typename FF>
691 const uint32_t& variable_index)
692{
693 bool is_not_constant = true;
694 const auto& constant_variable_indices = ultra_circuit_builder.constant_variable_indices;
695 for (const auto& pair : constant_variable_indices) {
696 if (pair.second == ultra_circuit_builder.real_variable_index[variable_index]) {
697 is_not_constant = false;
698 break;
699 }
700 }
701 return is_not_constant;
702}
703
715template <typename FF>
717 const std::vector<uint32_t>& variables_vector)
718{
719 if (variables_vector.empty()) {
720 return;
721 }
722 std::vector<uint32_t> filtered_variables_vector;
723 filtered_variables_vector.reserve(variables_vector.size());
724 // Only copy non-zero and non-constant variables
725 std::copy_if(variables_vector.begin(),
726 variables_vector.end(),
727 std::back_inserter(filtered_variables_vector),
728 [&](uint32_t variable_index) {
729 return variable_index != ultra_circuit_builder.zero_idx &&
730 this->check_is_not_constant_variable(ultra_circuit_builder, variable_index);
731 });
732 // Remove duplicates
733 auto unique_pointer = std::unique(filtered_variables_vector.begin(), filtered_variables_vector.end());
734 filtered_variables_vector.erase(unique_pointer, filtered_variables_vector.end());
735 if (filtered_variables_vector.size() < 2) {
736 return;
737 }
738 for (size_t i = 0; i < filtered_variables_vector.size() - 1; i++) {
739 this->add_new_edge(filtered_variables_vector[i], filtered_variables_vector[i + 1]);
740 }
741}
742
750template <typename FF>
751void StaticAnalyzer_<FF>::add_new_edge(const uint32_t& first_variable_index, const uint32_t& second_variable_index)
752{
753 variable_adjacency_lists[first_variable_index].emplace_back(second_variable_index);
754 variable_adjacency_lists[second_variable_index].emplace_back(first_variable_index);
755 variables_degree[first_variable_index] += 1;
756 variables_degree[second_variable_index] += 1;
757}
758
767template <typename FF>
768void StaticAnalyzer_<FF>::depth_first_search(const uint32_t& variable_index,
769 std::unordered_set<uint32_t>& is_used,
770 std::vector<uint32_t>& connected_component)
771{
772 std::stack<uint32_t> variable_stack;
773 variable_stack.push(variable_index);
774 while (!variable_stack.empty()) {
775 uint32_t current_index = variable_stack.top();
776 variable_stack.pop();
777 if (!is_used.contains(current_index)) {
778 is_used.insert(current_index);
779 connected_component.emplace_back(current_index);
780 for (const auto& it : variable_adjacency_lists[current_index]) {
781 variable_stack.push(it);
782 }
783 }
784 }
785}
786
795{
796 std::unordered_set<uint32_t> is_used;
797 std::vector<std::vector<uint32_t>> connected_components;
798 for (const auto& pair : variable_adjacency_lists) {
799 if (pair.first != 0 && variables_degree[pair.first] > 0) {
800 if (!is_used.contains(pair.first)) {
801 std::vector<uint32_t> connected_component;
802 this->depth_first_search(pair.first, is_used, connected_component);
803 std::sort(connected_component.begin(), connected_component.end());
804 connected_components.emplace_back(connected_component);
805 }
806 }
807 }
808 return connected_components;
809}
810
826template <typename FF>
828 std::unordered_set<uint32_t>& variables_in_one_gate,
829 size_t index)
830{
831 auto& arithmetic_block = ultra_circuit_constructor.blocks.arithmetic;
832 auto zero_idx = ultra_circuit_constructor.zero_idx;
833 size_t current_index = index;
834 std::vector<uint32_t> accumulators_indices;
835 while (true) {
836 // we have to remove left, right and output wires of the current gate, cause they'are new_limbs, and they are
837 // useless for the analyzer
838 auto fourth_idx = arithmetic_block.w_4()[current_index];
839 accumulators_indices.emplace_back(this->to_real(ultra_circuit_constructor, fourth_idx));
840 auto left_idx = arithmetic_block.w_l()[current_index];
841 if (left_idx != zero_idx) {
842 variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, left_idx));
843 }
844 auto right_idx = arithmetic_block.w_r()[current_index];
845 if (right_idx != zero_idx) {
846 variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, right_idx));
847 }
848 auto out_idx = arithmetic_block.w_o()[current_index];
849 if (out_idx != zero_idx) {
850 variables_in_one_gate.erase(this->to_real(ultra_circuit_constructor, out_idx));
851 }
852 auto q_arith = arithmetic_block.q_arith()[current_index];
853 if (q_arith == 1 || current_index == arithmetic_block.size() - 1) {
854 // this is the last gate in this chain, or we can't go next, so we have to stop a loop
855 break;
856 }
857 current_index++;
858 }
859 for (size_t i = 0; i < accumulators_indices.size(); i++) {
860 if (i == 0) {
861 // the first variable in accumulators is the variable which decompose was created. So, we have to decrement
862 // variable_gate_counts for this variable
863 variables_gate_counts[accumulators_indices[i]] -= 1;
864 } else {
865 // next accumulators are useless variables that are not interested for the analyzer. So, for these variables
866 // we can nullify variables_gate_counts
867 variables_gate_counts[accumulators_indices[i]] = 0;
868 }
869 }
870 // we don't want to make variables_gate_counts for intermediate variables negative, so, can go to the next gates
871 return current_index;
872}
873
882template <typename FF>
884 bb::UltraCircuitBuilder& ultra_circuit_builder,
885 std::unordered_set<uint32_t>& variables_in_one_gate,
886 const std::unordered_set<uint32_t>& decompose_variables)
887{
888 auto is_power_two = [&](const uint256_t& number) { return number > 0 && ((number & (number - 1)) == 0); };
889 auto find_position = [&](uint32_t variable_index) {
890 return decompose_variables.contains(this->to_real(ultra_circuit_builder, variable_index));
891 };
892 auto& arithmetic_block = ultra_circuit_builder.blocks.arithmetic;
893 if (arithmetic_block.size() > 0) {
894 for (size_t i = 0; i < arithmetic_block.size(); i++) {
895 auto q_1 = arithmetic_block.q_1()[i];
896 auto q_2 = arithmetic_block.q_2()[i];
897 auto q_3 = arithmetic_block.q_3()[i];
898 // big addition gate from decompose has selectors, which have the next property:
899 // q_1 = (1) << shifts[0], target_range_bitnum * (3 * i),
900 // q_2 = (1) << shifts[1], target_range_bitnum * (3 * i + 1),
901 // q_3 = (1) << shifts[2], target_range_bitnum * (3 * i + 2)
902 // so, they are power of two and satisfying the following equality: q_2 * q_2 = q_1 * q_3
903 // this way we can differ them from other arithmetic gates
904 bool q_1_is_power_two = is_power_two(q_1);
905 bool q_2_is_power_two = is_power_two(q_2);
906 bool q_3_is_power_two = is_power_two(q_3);
907 if (q_2 * q_2 == q_1 * q_3 && q_1_is_power_two && q_2_is_power_two && q_3_is_power_two) {
908 uint32_t left_idx = arithmetic_block.w_l()[i];
909 uint32_t right_idx = arithmetic_block.w_r()[i];
910 uint32_t out_idx = arithmetic_block.w_o()[i];
911 uint32_t fourth_idx = arithmetic_block.w_4()[i];
912 bool find_left = find_position(left_idx);
913 bool find_right = find_position(right_idx);
914 bool find_out = find_position(out_idx);
915 bool find_fourth = find_position(fourth_idx);
916 if (((find_left && find_right && find_out) || (find_left && find_right && !find_out) ||
917 (find_left && find_right && !find_out) || (find_left && !find_right && !find_out)) &&
918 !find_fourth) {
919 i = this->process_current_decompose_chain(ultra_circuit_builder, variables_in_one_gate, i);
920 }
921 }
922 }
923 }
924}
925
934template <typename FF>
936{
938 std::unordered_set<uint32_t> range_lists_tau_tags;
939 std::unordered_set<uint32_t> range_lists_range_tags;
940 std::vector<uint32_t> real_variable_tags = ultra_builder.real_variable_tags;
941 for (const auto& pair : range_lists) {
942 UltraCircuitBuilder::RangeList list = pair.second;
943 range_lists_tau_tags.insert(list.tau_tag);
944 range_lists_range_tags.insert(list.range_tag);
945 }
946 for (uint32_t real_index = 0; real_index < real_variable_tags.size(); real_index++) {
947 if (variables_in_one_gate.contains(real_index)) {
948 // this if helps us to remove variables from delta_range_constraints when finalize_circuit() function was
949 // called
950 if (range_lists_tau_tags.contains(real_variable_tags[real_index])) {
951 variables_in_one_gate.erase(real_index);
952 }
953 // this if helps us to remove variables from range_constraints when range_constraint_into_two_limbs function
954 // was called
955 if (range_lists_range_tags.contains(real_variable_tags[real_index])) {
956 variables_in_one_gate.erase(real_index);
957 }
958 }
959 }
960}
961
973template <typename FF>
975 std::unordered_set<uint32_t>& variables_in_one_gate,
976 UltraCircuitBuilder& ultra_circuit_builder,
977 BasicTableId& table_id,
978 size_t gate_index)
979{
980
981 auto find_position = [&](uint32_t real_variable_index) {
982 return variables_in_one_gate.contains(real_variable_index);
983 };
984 std::unordered_set<BasicTableId> aes_plookup_tables{ BasicTableId::AES_SBOX_MAP,
985 BasicTableId::AES_SPARSE_MAP,
986 BasicTableId::AES_SPARSE_NORMALIZE };
987 auto& lookup_block = ultra_circuit_builder.blocks.lookup;
988 if (aes_plookup_tables.contains(table_id)) {
989 uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]);
990 uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]);
991 if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) {
992 bool find_out = find_position(real_out_idx);
993 auto q_c = lookup_block.q_c()[gate_index];
994 if (q_c.is_zero()) {
995 if (find_out) {
996 variables_in_one_gate.erase(real_out_idx);
997 }
998 }
999 }
1000 }
1001}
1002
1015template <typename FF>
1017 std::unordered_set<uint32_t>& variables_in_one_gate,
1018 UltraCircuitBuilder& ultra_circuit_builder,
1019 BasicTableId& table_id,
1020 size_t gate_index)
1021{
1022
1023 auto find_position = [&](uint32_t real_variable_index) {
1024 return variables_in_one_gate.contains(real_variable_index);
1025 };
1026 auto& lookup_block = ultra_circuit_builder.blocks.lookup;
1027 std::unordered_set<BasicTableId> sha256_plookup_tables{ BasicTableId::SHA256_WITNESS_SLICE_3,
1028 BasicTableId::SHA256_WITNESS_SLICE_7_ROTATE_4,
1029 BasicTableId::SHA256_WITNESS_SLICE_8_ROTATE_7,
1030 BasicTableId::SHA256_WITNESS_SLICE_14_ROTATE_1,
1031 BasicTableId::SHA256_BASE16,
1032 BasicTableId::SHA256_BASE16_ROTATE2,
1033 BasicTableId::SHA256_BASE16_ROTATE6,
1034 BasicTableId::SHA256_BASE16_ROTATE7,
1035 BasicTableId::SHA256_BASE16_ROTATE8,
1036 BasicTableId::SHA256_BASE28,
1037 BasicTableId::SHA256_BASE28_ROTATE3,
1038 BasicTableId::SHA256_BASE28_ROTATE6 };
1039 if (sha256_plookup_tables.contains(table_id)) {
1040 uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]);
1041 uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]);
1042 if (variables_gate_counts[real_out_idx] != 1 || variables_gate_counts[real_right_idx] != 1) {
1043 // auto q_m = lookup_block.q_m()[gate_index];
1044 auto q_c = lookup_block.q_c()[gate_index];
1045 bool find_out = find_position(real_out_idx);
1046 // bool find_right = find_position(real_right_idx);
1047 if (q_c.is_zero()) {
1048 if (find_out) {
1049 variables_in_one_gate.erase(real_out_idx);
1050 }
1051 }
1052 if (table_id == SHA256_BASE16_ROTATE2 || table_id == SHA256_BASE28_ROTATE6) {
1053 // we want to remove false cases for special tables even though their selectors != 0
1054 // because they are used in read_from_1_to_2_table function, and they aren't dangerous
1055 variables_in_one_gate.erase(real_out_idx);
1056 }
1057 }
1058 }
1059}
1060
1071template <typename FF>
1073 size_t gate_index)
1074{
1075 auto find_position = [&](uint32_t real_variable_index) {
1076 return variables_in_one_gate.contains(real_variable_index);
1077 };
1078 auto& lookup_block = ultra_circuit_builder.blocks.lookup;
1079 auto& lookup_tables = ultra_circuit_builder.lookup_tables;
1080 auto table_index = static_cast<size_t>(lookup_block.q_3()[gate_index]);
1081 for (const auto& table : lookup_tables) {
1082 if (table.table_index == table_index) {
1083 std::unordered_set<bb::fr> column_1(table.column_1.begin(), table.column_1.end());
1084 std::unordered_set<bb::fr> column_2(table.column_2.begin(), table.column_2.end());
1085 std::unordered_set<bb::fr> column_3(table.column_3.begin(), table.column_3.end());
1086 bb::plookup::BasicTableId table_id = table.id;
1087 // false cases for AES
1088 this->remove_unnecessary_aes_plookup_variables(
1089 variables_in_one_gate, ultra_circuit_builder, table_id, gate_index);
1090 // false cases for sha256
1091 this->remove_unnecessary_sha256_plookup_variables(
1092 variables_in_one_gate, ultra_circuit_builder, table_id, gate_index);
1093 // if the amount of unique elements from columns of plookup tables = 1, it means that
1094 // variable from this column aren't used and we can remove it.
1095 if (column_1.size() == 1) {
1096 uint32_t left_idx = lookup_block.w_l()[gate_index];
1097 uint32_t real_left_idx = this->to_real(ultra_circuit_builder, left_idx);
1098 bool find_left = find_position(real_left_idx);
1099 if (find_left) {
1100 variables_in_one_gate.erase(real_left_idx);
1101 }
1102 }
1103 if (column_2.size() == 1) {
1104 uint32_t real_right_idx = this->to_real(ultra_circuit_builder, lookup_block.w_r()[gate_index]);
1105 bool find_right = find_position(real_right_idx);
1106 if (find_right) {
1107 variables_in_one_gate.erase(real_right_idx);
1108 }
1109 }
1110 if (column_3.size() == 1) {
1111 uint32_t real_out_idx = this->to_real(ultra_circuit_builder, lookup_block.w_o()[gate_index]);
1112 bool find_out = find_position(real_out_idx);
1113 if (find_out) {
1114 variables_in_one_gate.erase(real_out_idx);
1115 }
1116 }
1117 }
1118 }
1119}
1120
1128template <typename FF>
1130{
1131 auto& lookup_block = ultra_circuit_builder.blocks.lookup;
1132 if (lookup_block.size() > 0) {
1133 for (size_t i = 0; i < lookup_block.size(); i++) {
1134 this->process_current_plookup_gate(ultra_circuit_builder, i);
1135 }
1136 }
1137}
1138
1147template <typename FF>
1149{
1150 auto block_data = ultra_builder.blocks.get();
1151 size_t blk_idx = find_block_index(ultra_builder, ultra_builder.blocks.memory);
1152 std::vector<uint32_t> to_remove;
1153 BB_ASSERT_EQ(blk_idx, 5U);
1154 for (const auto& var_idx : variables_in_one_gate) {
1155 KeyPair key = { var_idx, blk_idx };
1156 if (auto search = variable_gates.find(key); search != variable_gates.end()) {
1157 std::vector<size_t> gate_indexes = variable_gates[key];
1158 BB_ASSERT_EQ(gate_indexes.size(), 1U);
1159 size_t gate_idx = gate_indexes[0];
1160 auto q_1 = block_data[blk_idx].q_1()[gate_idx];
1161 auto q_2 = block_data[blk_idx].q_2()[gate_idx];
1162 auto q_3 = block_data[blk_idx].q_3()[gate_idx];
1163 auto q_4 = block_data[blk_idx].q_4()[gate_idx];
1164 auto q_m = block_data[blk_idx].q_m()[gate_idx];
1165 auto q_arith = block_data[blk_idx].q_arith()[gate_idx];
1166 if (q_1 == FF::one() && q_m == FF::one() && q_2.is_zero() && q_3.is_zero() && q_4.is_zero() &&
1167 q_arith.is_zero()) {
1168 // record witness can be in both ROM and RAM gates, so we can ignore q_c
1169 // record witness is written as 4th variable in RAM/ROM read/write gate, so we can get 4th wire value
1170 // and check it with our variable
1171 if (this->to_real(ultra_builder, block_data[blk_idx].w_4()[gate_idx]) == var_idx) {
1172 to_remove.emplace_back(var_idx);
1173 }
1174 }
1175 }
1176 }
1177 for (const auto& elem : to_remove) {
1178 variables_in_one_gate.erase(elem);
1179 }
1180}
1181
1189template <typename FF>
1191 bb::UltraCircuitBuilder& ultra_circuit_builder)
1192{
1193 for (const auto& pair : variables_gate_counts) {
1194 bool is_not_constant_variable = this->check_is_not_constant_variable(ultra_circuit_builder, pair.first);
1195 if (pair.second == 1 && pair.first != 0 && is_not_constant_variable) {
1196 this->variables_in_one_gate.insert(pair.first);
1197 }
1198 }
1199 auto range_lists = ultra_circuit_builder.range_lists;
1200 std::unordered_set<uint32_t> decompose_varialbes;
1201 for (auto& pair : range_lists) {
1202 for (auto& elem : pair.second.variable_indices) {
1203 bool is_not_constant_variable = this->check_is_not_constant_variable(ultra_circuit_builder, elem);
1204 if (variables_gate_counts[ultra_circuit_builder.real_variable_index[elem]] == 1 &&
1205 is_not_constant_variable) {
1206 decompose_varialbes.insert(ultra_circuit_builder.real_variable_index[elem]);
1207 }
1208 }
1209 }
1210 this->remove_unnecessary_decompose_variables(
1211 ultra_circuit_builder, this->variables_in_one_gate, decompose_varialbes);
1212 this->remove_unnecessary_plookup_variables(ultra_circuit_builder);
1213 this->remove_unnecessary_range_constrains_variables(ultra_circuit_builder);
1214 for (const auto& elem : this->fixed_variables) {
1215 this->variables_in_one_gate.erase(elem);
1216 }
1217 // we found variables that were in one gate and they are intended cases.
1218 // so we have to remove them from the scope
1219 for (const auto& elem : ultra_circuit_builder.get_used_witnesses()) {
1220 this->variables_in_one_gate.erase(elem);
1221 }
1222 this->remove_record_witness_variables(ultra_circuit_builder);
1223 return variables_in_one_gate;
1224}
1225
1234 const std::vector<std::vector<uint32_t>>& connected_components, size_t index)
1235{
1236 auto connected_component = connected_components[index];
1237 auto size = connected_component.size();
1238 return std::make_pair(connected_component, size);
1239}
1240
1252template <typename FF> void StaticAnalyzer_<FF>::print_graph()
1253{
1254 for (const auto& elem : variable_adjacency_lists) {
1255 info("variable with index ", elem.first);
1256 if (variable_adjacency_lists[elem.first].empty()) {
1257 info("is isolated");
1258 } else {
1259 for (const auto& it : elem.second) {
1260 info(it);
1261 }
1262 }
1263 }
1264}
1265
1272{
1273 auto connected_components = find_connected_components();
1274 for (size_t i = 0; i < connected_components.size(); i++) {
1275 info("printing the ", i + 1, " connected component with size ", connected_components[i].size(), ":");
1276 for (const auto& it : connected_components[i]) {
1277 info(it, " ");
1278 }
1279 }
1280}
1281
1288{
1289 for (const auto& it : variables_gate_counts) {
1290 info("number of gates with variables ", it.first, " == ", it.second);
1291 }
1292}
1293
1301template <typename FF>
1303{
1304 const auto& block_data = ultra_builder.blocks.get();
1305 for (const auto& [key, gates] : variable_gates) {
1306 if (key.first == real_idx) {
1307 BB_ASSERT_EQ(gates.size(), 1U);
1308 size_t gate_index = gates[0];
1309 UltraBlock block = block_data[key.second];
1310 info("---- printing variables in this gate");
1311 info("w_l == ",
1312 block.w_l()[gate_index],
1313 " w_r == ",
1314 block.w_r()[gate_index],
1315 " w_o == ",
1316 block.w_o()[gate_index],
1317 " w_4 == ",
1318 block.w_4()[gate_index]);
1319 info("---- printing gate selectors where variable with index ", key.first, " was found ----");
1320 auto q_m = block.q_m()[gate_index];
1321 if (!q_m.is_zero()) {
1322 info("q_m == ", q_m);
1323 }
1324 auto q_1 = block.q_1()[gate_index];
1325 if (!q_1.is_zero()) {
1326 info("q1 == ", q_1);
1327 }
1328 auto q_2 = block.q_2()[gate_index];
1329 if (!q_2.is_zero()) {
1330 info("q2 == ", q_2);
1331 }
1332 auto q_3 = block.q_3()[gate_index];
1333 if (!q_3.is_zero()) {
1334 info("q3 == ", q_3);
1335 }
1336 auto q_4 = block.q_4()[gate_index];
1337 if (!q_4.is_zero()) {
1338 info("q4 == ", q_4);
1339 }
1340 auto q_c = block.q_c()[gate_index];
1341 if (!q_c.is_zero()) {
1342 info("q_c == ", q_c);
1343 }
1344 auto q_arith = block.q_arith()[gate_index];
1345 if (!q_arith.is_zero()) {
1346 info("q_arith == ", q_arith);
1347 }
1348 auto q_delta_range = block.q_delta_range()[gate_index];
1349 if (!q_delta_range.is_zero()) {
1350 info("q_delta_range == ", q_delta_range);
1351 }
1352 auto q_elliptic = block.q_elliptic()[gate_index];
1353 if (!q_elliptic.is_zero()) {
1354 info("q_elliptic == ", q_elliptic);
1355 }
1356 auto q_memory = block.q_memory()[gate_index];
1357 if (!q_memory.is_zero()) {
1358 info("q_memory == ", q_memory);
1359 }
1360 auto q_nnf = block.q_nnf()[gate_index];
1361 if (!q_nnf.is_zero()) {
1362 info("q_nnf == ", q_nnf);
1363 }
1364 auto q_lookup_type = block.q_lookup_type()[gate_index];
1365 if (!q_lookup_type.is_zero()) {
1366 info("q_lookup_type == ", q_lookup_type);
1367 }
1368 auto q_poseidon2_external = block.q_poseidon2_external()[gate_index];
1369 if (!q_poseidon2_external.is_zero()) {
1370 info("q_poseidon2_external == ", q_poseidon2_external);
1371 }
1372 auto q_poseidon2_internal = block.q_poseidon2_internal()[gate_index];
1373 if (!q_poseidon2_internal.is_zero()) {
1374 info("q_poseidon2_internal == ", q_poseidon2_internal);
1375 }
1376 info("---- finished printing ----");
1377 }
1378 }
1379}
1380
1381template class StaticAnalyzer_<bb::fr>;
1382
1383} // namespace cdg
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
#define ASSERT(expression,...)
Definition assert.hpp:49
std::vector< uint32_t > real_variable_tags
std::vector< uint32_t > real_variable_index
std::vector< RomTranscript > rom_arrays
Each entry in ram_arrays represents an independent ROM table. RomTranscript tracks the current table ...
std::vector< RamTranscript > ram_arrays
Each entry in ram_arrays represents an independent RAM table. RamTranscript tracks the current table ...
void emplace_back(const FF &value)
Append a field element to the selector.
std::map< FF, uint32_t > constant_variable_indices
std::map< uint64_t, RangeList > range_lists
std::vector< plookup::BasicTable > lookup_tables
std::vector< uint32_t > get_used_witnesses() const
virtual Selector< fr > & q_poseidon2_internal()
virtual Selector< fr > & q_elliptic()
virtual Selector< fr > & q_arith()
virtual Selector< fr > & q_poseidon2_external()
virtual Selector< fr > & q_lookup_type()
virtual Selector< fr > & q_nnf()
virtual Selector< fr > & q_delta_range()
virtual Selector< fr > & q_memory()
std::vector< uint32_t > get_poseido2s_gate_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock &blk)
this method creates connected components from poseidon2 gates
Definition graph.cpp:278
std::unordered_set< uint32_t > show_variables_in_one_gate(bb::UltraCircuitBuilder &ultra_circuit_builder)
this method returns a final set of variables that were in one gate
Definition graph.cpp:1190
std::vector< uint32_t > get_rom_table_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, const bb::RomTranscript &rom_array)
this method gets the ROM table connected component by processing ROM transcript records
Definition graph.cpp:460
size_t process_current_decompose_chain(bb::UltraCircuitBuilder &ultra_circuit_constructor, std::unordered_set< uint32_t > &variables_in_one_gate, size_t index)
this method removes variables that were created in a function decompose_into_default_range because th...
Definition graph.cpp:827
std::vector< std::vector< uint32_t > > find_connected_components()
this methond finds all connected components in the graph described by adjacency lists
Definition graph.cpp:794
std::vector< uint32_t > get_memory_gate_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock &blk)
this method creates connected components from Memory gates (RAM and ROM consistency checks)
Definition graph.cpp:312
void remove_unnecessary_plookup_variables(bb::UltraCircuitBuilder &ultra_circuit_builder)
this method removes false cases plookup variables from variables in one gate
Definition graph.cpp:1129
void remove_unnecessary_aes_plookup_variables(std::unordered_set< uint32_t > &variables_in_one_gate, bb::UltraCircuitBuilder &ultra_circuit_builder, 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:974
void print_graph()
this method prints graph as vertices and their adjacency lists example: we have an undirected graph f...
Definition graph.cpp:1252
std::vector< uint32_t > get_ram_table_connected_component(bb::UltraCircuitBuilder &ultra_builder, const bb::RamTranscript &ram_array)
this method gets the RAM table connected component by processing RAM transcript records
Definition graph.cpp:519
void print_variable_in_one_gate(bb::UltraCircuitBuilder &ultra_builder, const uint32_t real_idx)
this method prints all information about the gate where variable was found
Definition graph.cpp:1302
std::vector< uint32_t > get_non_native_field_gate_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock &blk)
this method creates connected components from Non-Native Field gates (bigfield operations)
Definition graph.cpp:379
void print_variables_gate_counts()
this method prints a number of gates for each variable
Definition graph.cpp:1287
std::vector< std::vector< uint32_t > > get_arithmetic_gate_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock &blk)
this method creates connected components from arithmetic gates
Definition graph.cpp:79
bool check_is_not_constant_variable(bb::UltraCircuitBuilder &ultra_circuit_builder, const uint32_t &variable_index)
this method checks whether the variable with given index is not constant
Definition graph.cpp:690
void print_connected_components()
this method prints all connected components that were found in the graph
Definition graph.cpp:1271
std::vector< uint32_t > get_sort_constraint_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock &blk)
this method creates connected components from sorted constraints
Definition graph.cpp:210
std::vector< uint32_t > get_plookup_gate_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock &blk)
this method creates connected components from plookup gates
Definition graph.cpp:238
void remove_unnecessary_range_constrains_variables(bb::UltraCircuitBuilder &ultra_builder)
this method removes variables from range constraints that are not security critical
Definition graph.cpp:935
size_t find_block_index(bb::UltraCircuitBuilder &ultra_builder, const UltraBlock &block)
this method finds index of the block in circuit builder by comparing pointers to blocks
Definition graph.cpp:21
void remove_unnecessary_decompose_variables(bb::UltraCircuitBuilder &ultra_circuit_builder, std::unordered_set< uint32_t > &variables_in_on_gate, const std::unordered_set< uint32_t > &decompose_variables)
this method removes unnecessary variables from decompose chains
Definition graph.cpp:883
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:768
void remove_record_witness_variables(bb::UltraCircuitBuilder &ultra_builder)
this method removes record witness variables from variables in one gate. initially record witness is ...
Definition graph.cpp:1148
void connect_all_variables_in_vector(bb::UltraCircuitBuilder &ultra_circuit_builder, 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:716
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:751
std::vector< uint32_t > get_elliptic_gate_connected_component(bb::UltraCircuitBuilder &ultra_circuit_builder, size_t index, size_t block_idx, UltraBlock &blk)
this method creates connected components from elliptic gates
Definition graph.cpp:166
void process_current_plookup_gate(bb::UltraCircuitBuilder &ultra_circuit_builder, 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:1072
void remove_unnecessary_sha256_plookup_variables(std::unordered_set< uint32_t > &variables_in_one_gate, bb::UltraCircuitBuilder &ultra_circuit_builder, 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:1016
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:49
StaticAnalyzer_()=default
void info(Args... args)
Definition log.hpp:70
@ SHA256_BASE16_ROTATE2
Definition types.hpp:37
@ SHA256_BASE28_ROTATE6
Definition types.hpp:34
Entry point for Barretenberg command-line interface.
typename Flavor::FF FF
Definition graph.cpp:11
std::pair< uint32_t, size_t > KeyPair
Definition graph.hpp:28
std::pair< std::vector< uint32_t >, size_t > get_connected_component_with_index(const std::vector< std::vector< uint32_t > > &connected_components, size_t index)
this method returns connected component with a given index and its size
Definition graph.cpp:1233
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Each ram array is an instance of memory transcript. It saves values and indexes for a particular memo...
std::vector< RamRecord > records
Each rom array is an instance of memory transcript. It saves values and indexes for a particular memo...
std::vector< RomRecord > records