23 auto blocks_data = ultra_builder.
blocks.get();
25 for (
size_t i = 0; i < blocks_data.size(); i++) {
26 if ((
void*)(&blocks_data[i]) == (
void*)(&block)) {
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()) {
58 for (
auto& var_idx : gate_variables) {
60 variable_gates[
key].emplace_back(gate_index);
62 for (
const auto& variable_index : gate_variables) {
63 variables_gate_counts[variable_index] += 1;
82 auto q_arith = blk.
q_arith()[index];
83 std::vector<uint32_t> gate_variables;
84 std::vector<uint32_t> minigate_variables;
86 if (q_arith.is_zero()) {
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];
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()) {
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()) {
104 if (!q_m.is_zero()) {
105 gate_variables.emplace_back(left_idx);
106 gate_variables.emplace_back(right_idx);
108 if (!q_1.is_zero()) {
109 gate_variables.emplace_back(left_idx);
111 if (!q_2.is_zero()) {
112 gate_variables.emplace_back(right_idx);
116 if (!q_3.is_zero()) {
117 gate_variables.emplace_back(out_idx);
119 if (!q_4.is_zero()) {
120 gate_variables.emplace_back(fourth_idx);
122 if (q_arith ==
FF(2)) {
126 if (index != blk.
size() - 1) {
127 gate_variables.emplace_back(blk.
w_4()[index + 1]);
130 if (q_arith ==
FF(3)) {
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]);
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);
151 return all_gates_variables;
165template <
typename FF>
169 std::vector<uint32_t> gate_variables;
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) {
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]);
186 if (is_elliptic_dbl_gate) {
188 gate_variables.emplace_back(blk.
w_r()[index + 1]);
189 gate_variables.emplace_back(blk.
w_o()[index + 1]);
192 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
193 this->process_gate_variables(gate_variables, index, block_idx);
195 return gate_variables;
209template <
typename FF>
213 std::vector<uint32_t> gate_variables = {};
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]);
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;
237template <
typename FF>
241 std::vector<uint32_t> gate_variables;
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];
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]);
255 if (!q_m.is_zero()) {
256 gate_variables.emplace_back(block.
w_r()[index + 1]);
258 if (!q_c.is_zero()) {
259 gate_variables.emplace_back(block.
w_o()[index + 1]);
262 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
263 this->process_gate_variables(gate_variables, index, blk_idx);
265 return gate_variables;
277template <
typename FF>
281 std::vector<uint32_t> gate_variables;
284 if (!internal_selector.is_zero() || !external_selector.is_zero()) {
285 gate_variables.reserve(8);
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]);
296 gate_variables = this->to_real(ultra_circuit_builder, gate_variables);
297 this->process_gate_variables(gate_variables, index, blk_idx);
299 return gate_variables;
311template <
typename FF>
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];
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];
330 if (q_1 == FF::one() && q_4 == FF::one()) {
333 if (index < block.
size() - 1) {
334 gate_variables.insert(gate_variables.end(),
335 { block.w_r()[index + 1],
338 block.w_l()[index + 1],
339 block.w_o()[index] });
341 }
else if (q_1 == FF::one() && q_2 == FF::one()) {
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] });
351 if (!q_3.is_zero()) {
352 if (index < block.
size() - 1) {
353 gate_variables.insert(gate_variables.end(),
354 { block.w_o()[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] });
364 gate_variables = this->to_real(ultra_builder, gate_variables);
365 this->process_gate_variables(gate_variables, index, blk_idx);
366 return gate_variables;
378template <
typename FF>
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];
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()) {
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] });
402 }
else if (q_3 == FF::one() && q_m == FF::one()) {
404 if (index < block.
size() - 1) {
405 gate_variables.insert(gate_variables.end(),
408 block.w_l()[index + 1],
409 block.w_r()[index + 1],
410 block.w_o()[index + 1],
411 block.w_4()[index + 1] });
413 }
else if (q_2 == FF::one() && (q_3 == FF::one() || q_4 == FF::one() || q_m == FF::one())) {
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]
419 if (q_3 == FF::one()) {
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 });
426 if (q_4 == FF::one()) {
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());
436 if (q_m == FF::one()) {
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] });
447 gate_variables = this->to_real(ultra_builder, gate_variables);
448 this->process_gate_variables(gate_variables, index, blk_idx);
449 return gate_variables;
459template <
typename FF>
463 size_t block_index = find_block_index(ultra_builder, ultra_builder.
blocks.memory);
470 std::vector<uint32_t> rom_table_variables;
472 for (
const auto& record : rom_array.
records) {
473 std::vector<uint32_t> gate_variables;
474 size_t gate_index = record.gate_index;
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];
483 auto index_witness = record.index_witness;
484 auto vc1_witness = record.value_column1_witness;
485 auto vc2_witness = record.value_column2_witness;
486 auto record_witness = record.record_witness;
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()) {
491 gate_variables.emplace_back(index_witness);
492 if (vc1_witness != ultra_builder.
zero_idx) {
493 gate_variables.emplace_back(vc1_witness);
495 if (vc2_witness != ultra_builder.
zero_idx) {
496 gate_variables.emplace_back(vc2_witness);
498 gate_variables.emplace_back(record_witness);
500 gate_variables = this->to_real(ultra_builder, gate_variables);
501 this->process_gate_variables(gate_variables, gate_index, block_index);
504 if (!gate_variables.empty()) {
505 rom_table_variables.insert(rom_table_variables.end(), gate_variables.begin(), gate_variables.end());
508 return rom_table_variables;
518template <
typename FF>
522 size_t block_index = find_block_index(ultra_builder, ultra_builder.
blocks.memory);
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;
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];
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;
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())) {
545 gate_variables.emplace_back(index_witness);
546 if (timestamp_witness != ultra_builder.
zero_idx) {
547 gate_variables.emplace_back(timestamp_witness);
549 if (value_witness != ultra_builder.
zero_idx) {
550 gate_variables.emplace_back(value_witness);
552 gate_variables.emplace_back(record_witness);
554 gate_variables = this->to_real(ultra_builder, gate_variables);
555 this->process_gate_variables(gate_variables, gate_index, block_index);
558 ram_table_variables.insert(ram_table_variables.end(), gate_variables.begin(), gate_variables.end());
560 return ram_table_variables;
583template <
typename FF>
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());
592 variables_gate_counts[variable_index] = 0;
593 variables_degree[variable_index] = 0;
594 variable_adjacency_lists[variable_index] = {};
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) {
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);
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);
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);
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);
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);
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);
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()) {
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);
648 sorted_variables.clear();
650 sorted_variables.insert(
651 sorted_variables.end(), delta_range_gate_variables.begin(), delta_range_gate_variables.end());
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);
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);
689template <
typename FF>
691 const uint32_t& variable_index)
693 bool is_not_constant =
true;
695 for (
const auto& pair : constant_variable_indices) {
697 is_not_constant =
false;
701 return is_not_constant;
715template <
typename FF>
717 const std::vector<uint32_t>& variables_vector)
719 if (variables_vector.empty()) {
722 std::vector<uint32_t> filtered_variables_vector;
723 filtered_variables_vector.reserve(variables_vector.size());
726 variables_vector.end(),
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);
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) {
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]);
750template <
typename FF>
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;
767template <
typename FF>
769 std::unordered_set<uint32_t>& is_used,
770 std::vector<uint32_t>& connected_component)
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);
796 std::unordered_set<uint32_t> is_used;
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);
808 return connected_components;
826template <
typename FF>
828 std::unordered_set<uint32_t>& variables_in_one_gate,
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;
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));
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));
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));
852 auto q_arith = arithmetic_block.q_arith()[current_index];
853 if (q_arith == 1 || current_index == arithmetic_block.size() - 1) {
859 for (
size_t i = 0; i < accumulators_indices.size(); i++) {
863 variables_gate_counts[accumulators_indices[i]] -= 1;
867 variables_gate_counts[accumulators_indices[i]] = 0;
871 return current_index;
882template <
typename FF>
885 std::unordered_set<uint32_t>& variables_in_one_gate,
886 const std::unordered_set<uint32_t>& decompose_variables)
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));
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];
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)) &&
919 i = this->process_current_decompose_chain(ultra_circuit_builder, variables_in_one_gate, i);
934template <
typename FF>
938 std::unordered_set<uint32_t> range_lists_tau_tags;
939 std::unordered_set<uint32_t> range_lists_range_tags;
941 for (
const auto& pair : range_lists) {
943 range_lists_tau_tags.insert(list.
tau_tag);
944 range_lists_range_tags.insert(list.
range_tag);
946 for (uint32_t real_index = 0; real_index < real_variable_tags.size(); real_index++) {
947 if (variables_in_one_gate.contains(real_index)) {
950 if (range_lists_tau_tags.contains(real_variable_tags[real_index])) {
951 variables_in_one_gate.erase(real_index);
955 if (range_lists_range_tags.contains(real_variable_tags[real_index])) {
956 variables_in_one_gate.erase(real_index);
973template <
typename FF>
975 std::unordered_set<uint32_t>& variables_in_one_gate,
981 auto find_position = [&](uint32_t real_variable_index) {
982 return variables_in_one_gate.contains(real_variable_index);
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];
996 variables_in_one_gate.erase(real_out_idx);
1015template <
typename FF>
1017 std::unordered_set<uint32_t>& variables_in_one_gate,
1023 auto find_position = [&](uint32_t real_variable_index) {
1024 return variables_in_one_gate.contains(real_variable_index);
1026 auto& lookup_block = ultra_circuit_builder.
blocks.lookup;
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) {
1044 auto q_c = lookup_block.q_c()[gate_index];
1045 bool find_out = find_position(real_out_idx);
1047 if (q_c.is_zero()) {
1049 variables_in_one_gate.erase(real_out_idx);
1055 variables_in_one_gate.erase(real_out_idx);
1071template <
typename FF>
1075 auto find_position = [&](uint32_t real_variable_index) {
1076 return variables_in_one_gate.contains(real_variable_index);
1078 auto& lookup_block = ultra_circuit_builder.
blocks.lookup;
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) {
1088 this->remove_unnecessary_aes_plookup_variables(
1089 variables_in_one_gate, ultra_circuit_builder, table_id, gate_index);
1091 this->remove_unnecessary_sha256_plookup_variables(
1092 variables_in_one_gate, ultra_circuit_builder, table_id, gate_index);
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);
1100 variables_in_one_gate.erase(real_left_idx);
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);
1107 variables_in_one_gate.erase(real_right_idx);
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);
1114 variables_in_one_gate.erase(real_out_idx);
1128template <
typename FF>
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);
1147template <
typename FF>
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;
1154 for (
const auto& var_idx : variables_in_one_gate) {
1156 if (
auto search = variable_gates.find(
key); search != variable_gates.end()) {
1157 std::vector<size_t> gate_indexes = variable_gates[
key];
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()) {
1171 if (this->to_real(ultra_builder, block_data[blk_idx].w_4()[gate_idx]) == var_idx) {
1172 to_remove.emplace_back(var_idx);
1177 for (
const auto& elem : to_remove) {
1178 variables_in_one_gate.erase(elem);
1189template <
typename FF>
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);
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);
1205 is_not_constant_variable) {
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);
1220 this->variables_in_one_gate.erase(elem);
1222 this->remove_record_witness_variables(ultra_circuit_builder);
1223 return variables_in_one_gate;
1234 const std::vector<std::vector<uint32_t>>& connected_components,
size_t index)
1236 auto connected_component = connected_components[index];
1237 auto size = connected_component.size();
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");
1259 for (
const auto& it : elem.second) {
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]) {
1289 for (
const auto& it : variables_gate_counts) {
1290 info(
"number of gates with variables ", it.first,
" == ", it.second);
1301template <
typename FF>
1304 const auto& block_data = ultra_builder.
blocks.get();
1305 for (
const auto& [
key, gates] : variable_gates) {
1306 if (
key.first == real_idx) {
1308 size_t gate_index = gates[0];
1310 info(
"---- printing variables in this gate");
1312 block.
w_l()[gate_index],
1314 block.
w_r()[gate_index],
1316 block.
w_o()[gate_index],
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);
1324 auto q_1 = block.
q_1()[gate_index];
1325 if (!q_1.is_zero()) {
1326 info(
"q1 == ", q_1);
1328 auto q_2 = block.
q_2()[gate_index];
1329 if (!q_2.is_zero()) {
1330 info(
"q2 == ", q_2);
1332 auto q_3 = block.
q_3()[gate_index];
1333 if (!q_3.is_zero()) {
1334 info(
"q3 == ", q_3);
1336 auto q_4 = block.
q_4()[gate_index];
1337 if (!q_4.is_zero()) {
1338 info(
"q4 == ", q_4);
1340 auto q_c = block.
q_c()[gate_index];
1341 if (!q_c.is_zero()) {
1342 info(
"q_c == ", q_c);
1344 auto q_arith = block.
q_arith()[gate_index];
1345 if (!q_arith.is_zero()) {
1346 info(
"q_arith == ", q_arith);
1349 if (!q_delta_range.is_zero()) {
1350 info(
"q_delta_range == ", q_delta_range);
1352 auto q_elliptic = block.
q_elliptic()[gate_index];
1353 if (!q_elliptic.is_zero()) {
1354 info(
"q_elliptic == ", q_elliptic);
1356 auto q_memory = block.
q_memory()[gate_index];
1357 if (!q_memory.is_zero()) {
1358 info(
"q_memory == ", q_memory);
1360 auto q_nnf = block.
q_nnf()[gate_index];
1361 if (!q_nnf.is_zero()) {
1362 info(
"q_nnf == ", q_nnf);
1365 if (!q_lookup_type.is_zero()) {
1366 info(
"q_lookup_type == ", q_lookup_type);
1369 if (!q_poseidon2_external.is_zero()) {
1370 info(
"q_poseidon2_external == ", q_poseidon2_external);
1373 if (!q_poseidon2_internal.is_zero()) {
1374 info(
"q_poseidon2_internal == ", q_poseidon2_internal);
1376 info(
"---- finished printing ----");
#define BB_ASSERT_EQ(actual, expected,...)
#define ASSERT(expression,...)
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
RomRamLogic rom_ram_logic
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
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
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
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...
std::vector< std::vector< uint32_t > > find_connected_components()
this methond finds all connected components in the graph described by adjacency lists
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)
void remove_unnecessary_plookup_variables(bb::UltraCircuitBuilder &ultra_circuit_builder)
this method removes false cases plookup variables from variables in one gate
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,...
void print_graph()
this method prints graph as vertices and their adjacency lists example: we have an undirected graph f...
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
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
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)
void print_variables_gate_counts()
this method prints a number of gates for each variable
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
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
void print_connected_components()
this method prints all connected components that were found in the graph
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
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
void remove_unnecessary_range_constrains_variables(bb::UltraCircuitBuilder &ultra_builder)
this method removes variables from range constraints that are not security critical
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
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
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
void remove_record_witness_variables(bb::UltraCircuitBuilder &ultra_builder)
this method removes record witness variables from variables in one gate. initially record witness is ...
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,...
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
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
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...
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...
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
StaticAnalyzer_()=default
Entry point for Barretenberg command-line interface.
std::pair< uint32_t, size_t > KeyPair
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
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
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