21#include <unordered_map>
22#include <unordered_set>
53 if (!this->circuit_finalized) {
54 std::unordered_set<size_t> table_indices;
55 for (
const auto& table : lookup_tables) {
56 BB_ASSERT_GT(table.table_index, 0U,
"Lookup table indices must be positive");
57 BB_ASSERT(table_indices.insert(table.table_index).second,
58 "Lookup table indices must be unique within a circuit");
61 process_non_native_field_multiplications();
63 this->rom_ram_logic.process_ROM_arrays(
this);
64 this->rom_ram_logic.process_RAM_arrays(
this);
65 process_range_lists();
67 populate_public_inputs_block();
68 this->circuit_finalized =
true;
71 info(
"WARNING: Redundant call to finalize_circuit(). Is this intentional?");
83 for (
const auto& idx : this->public_inputs()) {
85 blocks.pub_inputs.append_gate({ .wires = { idx, idx, this->zero_idx(), this->zero_idx() } });
100 create_big_add_gate({ .a = in.
a,
103 .d = this->zero_idx(),
119template <
typename ExecutionTrace>
121 const bool include_next_gate_w_4)
123 this->assert_valid_variables({ in.
a, in.
b, in.
c, in.
d });
128 blocks.arithmetic.append_gate({ .wires = { in.
a, in.
b, in.
c, in.
d },
136 .gate_value = include_next_gate_w_4 ? 2 : 1 });
137 this->increment_num_gates();
148template <
typename ExecutionTrace>
150 const bool include_next_gate_w_4)
152 this->assert_valid_variables({ in.
a, in.
b, in.
c, in.
d });
153 blocks.arithmetic.append_gate({ .wires = { in.
a, in.
b, in.
c, in.
d },
160 .gate_value = include_next_gate_w_4 ? 2 : 1 });
161 this->increment_num_gates();
174template <
typename ExecutionTrace>
177 this->assert_valid_variables({ in.
a, in.
b, in.
c, in.
d });
186 auto& block_for_row = blocks.arithmetic;
198 block_for_row.append_gate(row);
200 this->increment_num_gates();
208template <
typename ExecutionTrace>
211 this->assert_valid_variables({ variable_index });
213 blocks.arithmetic.append_gate({ .wires = { variable_index, variable_index, this->zero_idx(), this->zero_idx() },
218 this->increment_num_gates();
227template <
typename ExecutionTrace>
230 this->assert_valid_variables({ in.
a, in.
b, in.
c });
232 blocks.arithmetic.append_gate({ .wires = { in.
a, in.
b, in.
c, this->zero_idx() },
240 this->increment_num_gates();
259template <
typename ExecutionTrace>
262 this->assert_valid_variables({ in.
x1, in.
x2, in.
x3, in.
y1, in.
y2, in.
y3 });
264 auto& block = blocks.elliptic;
271 bool can_fuse_into_previous_gate =
273 block.w_r()[block.size() - 1] == in.
x1 &&
274 block.w_o()[block.size() - 1] == in.
y1;
276 if (can_fuse_into_previous_gate) {
277 block.q_1().set(block.size() - 1, q_sign);
281 auto& block_for_row = block;
283 row.
wires = { this->zero_idx(), in.
x1, in.
y1, this->zero_idx() };
287 block_for_row.append_gate(row);
289 this->increment_num_gates();
292 create_unconstrained_gate(block, in.
x2, in.
x3, in.
y3, in.
y2);
311template <
typename ExecutionTrace>
314 this->assert_valid_variables({ in.
x1, in.
x3, in.
y1, in.
y3 });
316 auto& block = blocks.elliptic;
319 bool can_fuse_into_previous_gate =
321 block.w_r()[block.size() - 1] == in.
x1 &&
322 block.w_o()[block.size() - 1] == in.
y1;
325 if (can_fuse_into_previous_gate) {
327 block.q_m().set(block.size() - 1, 1);
330 auto& block_for_row = block;
332 row.wires = { this->zero_idx(), in.
x1, in.
y1, this->zero_idx() };
336 block_for_row.append_gate(row);
338 this->increment_num_gates();
341 create_unconstrained_gate(block, this->zero_idx(), in.
x3, in.
y3,
this->zero_idx());
350template <
typename ExecutionTrace>
353 this->assert_valid_variables({ witness_index });
356 update_used_witnesses(witness_index);
358 blocks.arithmetic.append_gate({ .wires = { witness_index, this->zero_idx(), this->zero_idx(), this->zero_idx() },
359 .q_c = -witness_value,
363 this->increment_num_gates();
366template <
typename ExecutionTrace>
369 if (constant_variable_indices.contains(variable)) {
370 return constant_variable_indices.at(variable);
372 uint32_t variable_index = this->add_variable(variable);
373 fix_witness(variable_index, variable);
374 constant_variable_indices.insert({ variable, variable_index });
375 return variable_index;
387template <
typename ExecutionTrace>
391 if (table.id ==
id) {
398 return lookup_tables.back();
402template <
typename ExecutionTrace>
407 BB_ASSERT_GT(table.table_index, 0U,
"Table index must be greater than 0");
409 lookup_tables.emplace_back(
std::move(table));
410 return &lookup_tables.back();
414template <
typename ExecutionTrace>
416 const uint32_t val1_idx,
417 const uint32_t val2_idx,
420 const FF column_1_step_size,
421 const FF column_2_step_size,
422 const FF column_3_step_size)
424 this->assert_valid_variables({ key_idx, val1_idx, val2_idx });
429 auto& block_for_row = blocks.lookup;
431 row.
wires = { key_idx, val1_idx, val2_idx, this->zero_idx() };
434 row.q_m = column_2_step_size;
435 row.q_c = column_3_step_size;
436 row.q_2 = column_1_step_size;
438 block_for_row.append_gate(row);
440 this->increment_num_gates();
470template <
typename ExecutionTrace>
474 const uint32_t key_a_index,
480 const size_t num_lookups = read_values[ColumnIdx::C1].size();
483 for (
size_t i = 0; i < num_lookups; ++i) {
484 const bool is_first_lookup = (i == 0);
485 const bool is_last_lookup = (i == num_lookups - 1);
491 const auto first_idx = is_first_lookup ? key_a_index : this->add_variable(read_values[ColumnIdx::C1][i]);
492 const auto second_idx = (is_first_lookup && key_b_index.has_value())
494 :
this->add_variable(read_values[ColumnIdx::C2][i]);
495 const auto third_idx = this->add_variable(read_values[ColumnIdx::C3][i]);
497 read_data[ColumnIdx::C1].push_back(first_idx);
498 read_data[ColumnIdx::C2].push_back(second_idx);
499 read_data[ColumnIdx::C3].push_back(third_idx);
502 const FF col1_step = is_last_lookup ?
FF(0) : -multi_table.column_1_step_sizes[i + 1];
503 const FF col2_step = is_last_lookup ?
FF(0) : -multi_table.column_2_step_sizes[i + 1];
504 const FF col3_step = is_last_lookup ?
FF(0) : -multi_table.column_3_step_sizes[i + 1];
507 first_idx, second_idx, third_idx, table, read_values.
lookup_entries[i], col1_step, col2_step, col3_step);
515template <
typename ExecutionTrace>
517 const uint64_t target_range)
520 const auto range_tag = get_new_tag();
521 const auto tau_tag = get_new_tag();
522 set_tau_transposition(range_tag, tau_tag);
523 result.target_range = target_range;
524 result.range_tag = range_tag;
527 uint64_t num_multiples_of_three = (target_range / DEFAULT_PLOOKUP_RANGE_STEP_SIZE);
531 result.variable_indices.reserve(
static_cast<uint32_t
>(num_multiples_of_three + 3));
532 for (uint64_t i = 0; i <= num_multiples_of_three; ++i) {
533 const uint32_t
index = this->add_variable(
fr(i * DEFAULT_PLOOKUP_RANGE_STEP_SIZE));
539 const uint32_t
index = this->add_variable(
fr(target_range));
544 create_unconstrained_gates(
result.variable_indices);
549template <
typename ExecutionTrace>
551 const uint32_t variable_index,
const uint64_t num_bits,
const uint64_t target_range_bitnum, std::string_view msg)
553 this->assert_valid_variables({ variable_index });
561 if (val.
get_msb() >= num_bits && !this->failed()) {
562 this->failure(std::string(msg));
566 const uint64_t sublimb_mask = (1ULL << target_range_bitnum) - 1;
568 std::vector<uint64_t> sublimbs;
569 std::vector<uint32_t> sublimb_indices;
571 const bool has_remainder_bits = (num_bits % target_range_bitnum != 0);
572 const uint64_t num_limbs = (num_bits / target_range_bitnum) + has_remainder_bits;
573 const uint64_t last_limb_size = num_bits - ((num_bits / target_range_bitnum) * target_range_bitnum);
574 const uint64_t last_limb_range = ((uint64_t)1 << last_limb_size) - 1;
578 for (
size_t i = 0; i < num_limbs; ++i) {
579 sublimbs.push_back(accumulator.data[0] & sublimb_mask);
580 accumulator = accumulator >> target_range_bitnum;
584 const size_t num_full_limbs = has_remainder_bits ? sublimbs.size() - 1 : sublimbs.size();
585 for (
size_t i = 0; i < num_full_limbs; ++i) {
586 const auto limb_idx = this->add_variable(
bb::fr(sublimbs[i]));
587 sublimb_indices.emplace_back(limb_idx);
588 create_small_range_constraint(limb_idx, sublimb_mask);
590 if (has_remainder_bits) {
591 const auto limb_idx = this->add_variable(
bb::fr(sublimbs.back()));
592 sublimb_indices.emplace_back(limb_idx);
593 create_small_range_constraint(limb_idx, last_limb_range);
601 const uint64_t num_limb_triples = (num_limbs / 3) + ((num_limbs % 3) != 0);
603 const uint64_t leftovers = (num_limbs % 3) == 0 ? 3 : (num_limbs % 3);
606 uint32_t accumulator_idx = variable_index;
609 for (
size_t i = 0; i < num_limb_triples; ++i) {
612 const bool real_limbs[3]{
613 !(i == (num_limb_triples - 1) && (leftovers < 1)),
614 !(i == (num_limb_triples - 1) && (leftovers < 2)),
615 !(i == (num_limb_triples - 1) && (leftovers < 3)),
619 const uint64_t round_sublimbs[3]{
620 real_limbs[0] ? sublimbs[3 * i] : 0,
621 real_limbs[1] ? sublimbs[3 * i + 1] : 0,
622 real_limbs[2] ? sublimbs[3 * i + 2] : 0,
625 const uint32_t new_limbs[3]{
626 real_limbs[0] ? sublimb_indices[3 * i] : this->zero_idx(),
627 real_limbs[1] ? sublimb_indices[3 * i + 1] : this->zero_idx(),
628 real_limbs[2] ? sublimb_indices[3 * i + 2] : this->zero_idx(),
631 const uint64_t shifts[3]{
632 target_range_bitnum * (3 * i),
633 target_range_bitnum * (3 * i + 1),
634 target_range_bitnum * (3 * i + 2),
639 (
uint256_t(round_sublimbs[1]) << shifts[1]) -
640 (
uint256_t(round_sublimbs[2]) << shifts[2]);
664 (i != num_limb_triples - 1));
665 if (i != num_limb_triples - 1) {
666 accumulator_idx = this->add_variable(
fr(new_accumulator));
670 return sublimb_indices;
673template <
typename ExecutionTrace>
675 const uint64_t target_range,
676 std::string_view msg)
680 const bool is_out_of_range = (
uint256_t(this->get_variable(variable_index)).
data[0] > target_range);
681 if (is_out_of_range && !this->failed()) {
682 this->failure(std::string(msg));
684 if (range_lists.count(target_range) == 0) {
685 range_lists.insert({ target_range, create_range_list(target_range) });
689 const auto existing_tag = this->real_variable_tags[this->real_variable_index[variable_index]];
690 auto& list = range_lists[target_range];
695 if (existing_tag == list.range_tag) {
700 if (existing_tag == DEFAULT_TAG) {
701 assign_tag(variable_index, list.range_tag);
702 list.variable_indices.emplace_back(variable_index);
706 bool found_tag =
false;
707 for (
const auto& r : range_lists) {
708 if (r.second.range_tag == existing_tag) {
710 if (r.first < target_range) {
721 const uint32_t copied_witness = this->add_variable(this->get_variable(variable_index));
722 create_add_gate({ .a = variable_index,
724 .c = this->zero_idx(),
728 .const_scaling = 0 });
730 create_small_range_constraint(copied_witness, target_range, msg);
748 x = this->real_variable_index[x];
757 std::vector<uint32_t> sorted_list;
761 const auto& field_element = this->get_variable(variable_index);
762 const uint32_t shrinked_value =
static_cast<uint32_t
>(field_element);
763 sorted_list.emplace_back(shrinked_value);
767 std::sort(sorted_list.begin(), sorted_list.end());
769 std::sort(std::execution::par_unseq, sorted_list.begin(), sorted_list.end());
772 constexpr size_t gate_width = NUM_WIRES;
773 size_t padding = (gate_width - (list.
variable_indices.size() % gate_width)) % gate_width;
775 std::vector<uint32_t> indices;
776 indices.reserve(padding + sorted_list.size());
779 padding += gate_width;
781 for (
size_t i = 0; i < padding; ++i) {
782 indices.emplace_back(this->zero_idx());
785 for (
const auto sorted_value : sorted_list) {
786 const uint32_t
index = this->add_variable(
fr(sorted_value));
788 indices.emplace_back(
index);
791 create_sort_constraint_with_edges(indices, 0, list.
target_range);
796 for (
auto& i : range_lists) {
797 process_range_list(i.second);
801template <
typename ExecutionTrace>
804 constexpr size_t gate_width = NUM_WIRES;
806 this->assert_valid_variables(variable_indices);
808 for (
size_t i = 0; i < variable_indices.size(); i += gate_width) {
810 this->increment_num_gates();
812 auto& block_for_row = blocks.delta_range;
815 variable_indices[i], variable_indices[i + 1], variable_indices[i + 2], variable_indices[i + 3]
819 block_for_row.append_gate(row);
823 create_unconstrained_gate(blocks.delta_range,
824 variable_indices[variable_indices.size() - 1],
832template <
typename ExecutionTrace>
835 std::vector<uint32_t> padded_list = variable_index;
836 constexpr size_t gate_width = NUM_WIRES;
837 const uint64_t padding = (gate_width - (padded_list.size() % gate_width)) % gate_width;
838 for (uint64_t i = 0; i < padding; ++i) {
839 padded_list.emplace_back(this->zero_idx());
841 this->assert_valid_variables(variable_index);
842 this->assert_valid_variables(padded_list);
844 for (
size_t i = 0; i < padded_list.size(); i += gate_width) {
845 create_unconstrained_gate(
846 blocks.arithmetic, padded_list[i], padded_list[i + 1], padded_list[i + 2], padded_list[i + 3]);
850template <
typename ExecutionTrace>
852 const std::vector<uint32_t>& variable_indices,
const FF& start,
const FF& end)
855 constexpr size_t gate_width = NUM_WIRES;
858 this->assert_valid_variables(variable_indices);
861 auto& block = blocks.delta_range;
864 create_add_gate({ variable_indices[0], this->zero_idx(), this->zero_idx(), 1, 0, 0, -start });
868 for (
size_t i = 0; i < variable_indices.size(); i += gate_width) {
870 this->increment_num_gates();
872 auto& block_for_row = block;
875 variable_indices[i], variable_indices[i + 1], variable_indices[i + 2], variable_indices[i + 3]
879 block_for_row.append_gate(row);
885 create_unconstrained_gate(
886 block, variable_indices[variable_indices.size() - 1],
this->zero_idx(),
this->zero_idx(),
this->zero_idx());
889 { variable_indices[variable_indices.size() - 1], this->zero_idx(), this->zero_idx(), 1, 0, 0, -end });
916template <
typename ExecutionTrace>
922 row.gate_value =
type == MEMORY_SELECTORS::MEM_NONE ? 0 : 1;
924 case MEMORY_SELECTORS::ROM_CONSISTENCY_CHECK: {
933 case MEMORY_SELECTORS::RAM_CONSISTENCY_CHECK: {
942 case MEMORY_SELECTORS::RAM_TIMESTAMP_CHECK: {
949 case MEMORY_SELECTORS::ROM_READ: {
958 case MEMORY_SELECTORS::RAM_READ: {
967 case MEMORY_SELECTORS::RAM_WRITE: {
976 case MEMORY_SELECTORS::ROM_LOGUP_TABLE: {
984 case MEMORY_SELECTORS::ROM_LOGUP_READ: {
1022template <
typename ExecutionTrace>
1028 row.gate_value =
type == NNF_SELECTORS::NNF_NONE ? 0 : 1;
1030 case NNF_SELECTORS::LIMB_ACCUMULATE_1: {
1035 case NNF_SELECTORS::LIMB_ACCUMULATE_2: {
1040 case NNF_SELECTORS::NON_NATIVE_FIELD_1: {
1045 case NNF_SELECTORS::NON_NATIVE_FIELD_2: {
1050 case NNF_SELECTORS::NON_NATIVE_FIELD_3: {
1072template <
typename ExecutionTrace>
1074 const uint32_t hi_idx,
1075 const size_t lo_limb_bits,
1076 const size_t hi_limb_bits,
1077 std::string_view msg)
1085 const bool is_lo_out_of_range = (
uint256_t(this->get_variable(lo_idx)) >= (
uint256_t(1) << lo_limb_bits));
1086 if (is_lo_out_of_range && !this->failed()) {
1087 this->failure(std::string(msg) +
": lo limb.");
1089 const bool is_hi_out_of_range = (
uint256_t(this->get_variable(hi_idx)) >= (
uint256_t(1) << hi_limb_bits));
1090 if (is_hi_out_of_range && !this->failed()) {
1091 this->failure(std::string(msg) +
": hi limb.");
1095 const auto get_sublimbs = [&](
const uint32_t& limb_idx,
const std::array<uint64_t, 5>& sublimb_masks) {
1096 const uint256_t limb = this->get_variable(limb_idx);
1102 sublimb_indices[0] = sublimb_masks[0] != 0 ? this->add_variable(
fr(limb & MAX_SUBLIMB_MASK)) : this->zero_idx();
1103 sublimb_indices[1] =
1104 sublimb_masks[1] != 0 ? this->add_variable(
fr((limb >> 14) & MAX_SUBLIMB_MASK)) : this->zero_idx();
1105 sublimb_indices[2] =
1106 sublimb_masks[2] != 0 ? this->add_variable(
fr((limb >> 28) & MAX_SUBLIMB_MASK)) : this->zero_idx();
1107 sublimb_indices[3] =
1108 sublimb_masks[3] != 0 ? this->add_variable(
fr((limb >> 42) & MAX_SUBLIMB_MASK)) : this->zero_idx();
1109 sublimb_indices[4] =
1110 sublimb_masks[4] != 0 ? this->add_variable(
fr((limb >> 56) & MAX_SUBLIMB_MASK)) : this->zero_idx();
1111 return sublimb_indices;
1114 const auto get_limb_masks = [](
size_t limb_bits) {
1115 std::array<uint64_t, 5> sublimb_masks;
1116 sublimb_masks[0] = limb_bits >= 14 ? 14 : limb_bits;
1117 sublimb_masks[1] = limb_bits >= 28 ? 14 : (limb_bits > 14 ? limb_bits - 14 : 0);
1118 sublimb_masks[2] = limb_bits >= 42 ? 14 : (limb_bits > 28 ? limb_bits - 28 : 0);
1119 sublimb_masks[3] = limb_bits >= 56 ? 14 : (limb_bits > 42 ? limb_bits - 42 : 0);
1120 sublimb_masks[4] = (limb_bits > 56 ? limb_bits - 56 : 0);
1122 for (
auto& mask : sublimb_masks) {
1123 mask = (1ULL << mask) - 1ULL;
1125 return sublimb_masks;
1128 const auto lo_masks = get_limb_masks(lo_limb_bits);
1129 const auto hi_masks = get_limb_masks(hi_limb_bits);
1134 auto row = nnf_selectors_row(NNF_SELECTORS::LIMB_ACCUMULATE_1);
1135 row.wires = { lo_sublimbs[0], lo_sublimbs[1], lo_sublimbs[2], lo_idx };
1136 blocks.nnf.append_gate(row);
1139 auto row = nnf_selectors_row(NNF_SELECTORS::LIMB_ACCUMULATE_2);
1140 row.wires = { lo_sublimbs[3], lo_sublimbs[4], hi_sublimbs[0], hi_sublimbs[1] };
1141 blocks.nnf.append_gate(row);
1144 auto row = nnf_selectors_row(NNF_SELECTORS::NNF_NONE);
1145 row.wires = { hi_sublimbs[2], hi_sublimbs[3], hi_sublimbs[4], hi_idx };
1146 blocks.nnf.append_gate(row);
1148 this->increment_num_gates(3);
1150 for (
size_t i = 0; i < 5; i++) {
1151 if (lo_masks[i] != 0) {
1152 create_small_range_constraint(
1153 lo_sublimbs[i], lo_masks[i],
"ultra_circuit_builder: sublimb of low too large");
1155 if (hi_masks[i] != 0) {
1156 create_small_range_constraint(
1157 hi_sublimbs[i], hi_masks[i],
"ultra_circuit_builder: sublimb of hi too large");
1177template <
typename ExecutionTrace>
1181 const auto [a0, a1, a2, a3] = std::array{ this->get_variable(input.
a[0]),
1182 this->get_variable(input.
a[1]),
1183 this->get_variable(input.
a[2]),
1184 this->get_variable(input.
a[3]) };
1185 const auto [b0, b1, b2, b3] = std::array{ this->get_variable(input.
b[0]),
1186 this->get_variable(input.
b[1]),
1187 this->get_variable(input.
b[2]),
1188 this->get_variable(input.
b[3]) };
1189 const auto [q0, q1, q2, q3] = std::array{ this->get_variable(input.
q[0]),
1190 this->get_variable(input.
q[1]),
1191 this->get_variable(input.
q[2]),
1192 this->get_variable(input.
q[3]) };
1193 const auto [r0, r1, r2, r3] = std::array{ this->get_variable(input.
r[0]),
1194 this->get_variable(input.
r[1]),
1195 this->get_variable(input.
r[2]),
1196 this->get_variable(input.
r[3]) };
1199 constexpr FF LIMB_SHIFT =
uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS;
1200 constexpr FF LIMB_RSHIFT =
FF(1) /
FF(
uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS);
1201 constexpr FF LIMB_RSHIFT_2 =
FF(1) /
FF(
uint256_t(1) << (2 * DEFAULT_NON_NATIVE_FIELD_LIMB_BITS));
1204 FF lo_0 = (a0 * b0 - r0) + (a1 * b0 + a0 * b1) * LIMB_SHIFT;
1206 FF lo_1 = (lo_0 + q0 * p_neg[0] + (q1 * p_neg[0] + q0 * p_neg[1] - r1) * LIMB_SHIFT) * LIMB_RSHIFT_2;
1209 FF hi_0 = (a2 * b0 + a0 * b2) + (a0 * b3 + a3 * b0 - r3) * LIMB_SHIFT;
1211 FF hi_1 = hi_0 + (a1 * b1 - r2) + (a1 * b2 + a2 * b1) * LIMB_SHIFT;
1213 FF hi_2 = hi_1 + lo_1 + q2 * p_neg[0] + (q3 * p_neg[0] + q2 * p_neg[1]) * LIMB_SHIFT;
1215 FF hi_3 = (hi_2 + q0 * p_neg[2] + q1 * p_neg[1] + (q0 * p_neg[3] + q1 * p_neg[2]) * LIMB_SHIFT) * LIMB_RSHIFT_2;
1217 const uint32_t lo_0_idx = this->add_variable(lo_0);
1218 const uint32_t lo_1_idx = this->add_variable(lo_1);
1219 const uint32_t hi_0_idx = this->add_variable(hi_0);
1220 const uint32_t hi_1_idx = this->add_variable(hi_1);
1221 const uint32_t hi_2_idx = this->add_variable(hi_2);
1222 const uint32_t hi_3_idx = this->add_variable(hi_3);
1229 create_big_add_gate({ input.
q[0],
1240 create_unconstrained_gate(blocks.arithmetic,
this->zero_idx(),
this->zero_idx(),
this->zero_idx(), lo_0_idx);
1258 auto row = nnf_selectors_row(NNF_SELECTORS::NON_NATIVE_FIELD_1);
1259 row.wires = { input.
a[1], input.
b[1], input.
r[0], lo_0_idx };
1260 blocks.nnf.append_gate(row);
1262 this->increment_num_gates();
1276 auto row = nnf_selectors_row(NNF_SELECTORS::NON_NATIVE_FIELD_2);
1277 row.wires = { input.
a[0], input.
b[0], input.
a[3], input.
b[3] };
1278 blocks.nnf.append_gate(row);
1280 this->increment_num_gates();
1294 auto row = nnf_selectors_row(NNF_SELECTORS::NON_NATIVE_FIELD_3);
1295 row.wires = { input.
a[2], input.
b[2], input.
r[3], hi_0_idx };
1296 blocks.nnf.append_gate(row);
1298 this->increment_num_gates();
1305 auto row = nnf_selectors_row(NNF_SELECTORS::NNF_NONE);
1306 row.wires = { input.
a[1], input.
b[1], input.
r[2], hi_1_idx };
1307 blocks.nnf.append_gate(row);
1309 this->increment_num_gates();
1316 create_big_add_gate(
1335 create_big_add_gate({
1347 return std::array<uint32_t, 2>{ lo_1_idx, hi_3_idx };
1358 for (
size_t i = 0; i < cached_partial_non_native_field_multiplications.size(); ++i) {
1359 auto& c = cached_partial_non_native_field_multiplications[i];
1360 for (
size_t j = 0; j < c.a.size(); ++j) {
1361 c.a[j] = this->real_variable_index[c.a[j]];
1362 c.b[j] = this->real_variable_index[c.b[j]];
1365 cached_partial_non_native_field_multiplication::deduplicate(cached_partial_non_native_field_multiplications,
this);
1368 for (
const auto& input : cached_partial_non_native_field_multiplications) {
1371 auto row = nnf_selectors_row(NNF_SELECTORS::NON_NATIVE_FIELD_1);
1372 row.wires = { input.a[1], input.b[1], this->zero_idx(), input.lo_0 };
1373 blocks.nnf.append_gate(row);
1375 this->increment_num_gates();
1378 auto row = nnf_selectors_row(NNF_SELECTORS::NON_NATIVE_FIELD_2);
1379 row.wires = { input.a[0], input.b[0], input.a[3], input.b[3] };
1380 blocks.nnf.append_gate(row);
1382 this->increment_num_gates();
1385 auto row = nnf_selectors_row(NNF_SELECTORS::NON_NATIVE_FIELD_3);
1386 row.wires = { input.a[2], input.b[2], this->zero_idx(), input.hi_0 };
1387 blocks.nnf.append_gate(row);
1389 this->increment_num_gates();
1392 auto row = nnf_selectors_row(NNF_SELECTORS::NNF_NONE);
1393 row.wires = { input.a[1], input.b[1], this->zero_idx(), input.hi_1 };
1394 blocks.nnf.append_gate(row);
1396 this->increment_num_gates();
1406template <
typename ExecutionTrace>
1411 this->get_variable(input.
a[0]),
1412 this->get_variable(input.
a[1]),
1413 this->get_variable(input.
a[2]),
1414 this->get_variable(input.
a[3]),
1417 this->get_variable(input.
b[0]),
1418 this->get_variable(input.
b[1]),
1419 this->get_variable(input.
b[2]),
1420 this->get_variable(input.
b[3]),
1423 constexpr FF LIMB_SHIFT =
uint256_t(1) << DEFAULT_NON_NATIVE_FIELD_LIMB_BITS;
1425 FF lo_0 =
a[0] *
b[0] + ((
a[1] *
b[0] +
a[0] *
b[1]) * LIMB_SHIFT);
1426 FF hi_0 =
a[2] *
b[0] +
a[0] *
b[2] + ((
a[0] *
b[3] +
a[3] *
b[0]) * LIMB_SHIFT);
1427 FF hi_1 = hi_0 +
a[1] *
b[1] + ((
a[1] *
b[2] +
a[2] *
b[1]) * LIMB_SHIFT);
1429 const uint32_t lo_0_idx = this->add_variable(lo_0);
1430 const uint32_t hi_0_idx = this->add_variable(hi_0);
1431 const uint32_t hi_1_idx = this->add_variable(hi_1);
1441 cached_partial_non_native_field_multiplications.emplace_back(cache_entry);
1442 return std::array<uint32_t, 2>{ lo_0_idx, hi_1_idx };
1450template <
typename ExecutionTrace>
1484 const FF z_0value = (this->get_variable(x_0) * x_mulconst0) + (this->get_variable(y_0) * y_mulconst0) + addconst0;
1485 const FF z_1value = (this->get_variable(x_1) * x_mulconst1) + (this->get_variable(y_1) * y_mulconst1) + addconst1;
1486 const FF z_2value = (this->get_variable(x_2) * x_mulconst2) + (this->get_variable(y_2) * y_mulconst2) + addconst2;
1487 const FF z_3value = (this->get_variable(x_3) * x_mulconst3) + (this->get_variable(y_3) * y_mulconst3) + addconst3;
1488 const FF z_pvalue = this->get_variable(x_p) + this->get_variable(y_p) + addconstp;
1490 const uint32_t z_0 = this->add_variable(z_0value);
1491 const uint32_t z_1 = this->add_variable(z_1value);
1492 const uint32_t z_2 = this->add_variable(z_2value);
1493 const uint32_t z_3 = this->add_variable(z_3value);
1494 const uint32_t z_p = this->add_variable(z_pvalue);
1517 auto& block = blocks.arithmetic;
1522 const FF linear_term_scale_factor = 2;
1524 auto& block_for_row = block;
1526 row.
wires = { y_p, x_0, y_0, x_p };
1527 row.q_m = addconstp;
1528 row.q_c = -addconst0 * linear_term_scale_factor;
1529 row.q_2 = -x_mulconst0 * linear_term_scale_factor;
1530 row.q_3 = -y_mulconst0 * linear_term_scale_factor;
1533 block_for_row.append_gate(row);
1537 auto& block_for_row = block;
1539 row.
wires = { z_p, x_1, y_1, z_0 };
1540 row.q_c = -addconst1;
1541 row.q_2 = -x_mulconst1;
1542 row.q_3 = -y_mulconst1;
1545 block_for_row.append_gate(row);
1549 auto& block_for_row = block;
1551 row.
wires = { x_2, y_2, z_2, z_1 };
1552 row.q_c = -addconst2;
1553 row.q_1 = -x_mulconst2;
1554 row.q_2 = -y_mulconst2;
1558 block_for_row.append_gate(row);
1562 auto& block_for_row = block;
1564 row.
wires = { x_3, y_3, z_3, this->zero_idx() };
1565 row.q_c = -addconst3;
1566 row.q_1 = -x_mulconst3;
1567 row.q_2 = -y_mulconst3;
1571 block_for_row.append_gate(row);
1574 this->increment_num_gates(4);
1576 z_0, z_1, z_2, z_3, z_p,
1585template <
typename ExecutionTrace>
1619 const FF z_0value = (this->get_variable(x_0) * x_mulconst0) - (this->get_variable(y_0) * y_mulconst0) + addconst0;
1620 const FF z_1value = (this->get_variable(x_1) * x_mulconst1) - (this->get_variable(y_1) * y_mulconst1) + addconst1;
1621 const FF z_2value = (this->get_variable(x_2) * x_mulconst2) - (this->get_variable(y_2) * y_mulconst2) + addconst2;
1622 const FF z_3value = (this->get_variable(x_3) * x_mulconst3) - (this->get_variable(y_3) * y_mulconst3) + addconst3;
1623 const FF z_pvalue = this->get_variable(x_p) - this->get_variable(y_p) + addconstp;
1625 const uint32_t z_0 = this->add_variable(z_0value);
1626 const uint32_t z_1 = this->add_variable(z_1value);
1627 const uint32_t z_2 = this->add_variable(z_2value);
1628 const uint32_t z_3 = this->add_variable(z_3value);
1629 const uint32_t z_p = this->add_variable(z_pvalue);
1655 auto& block = blocks.arithmetic;
1660 const FF linear_term_scale_factor = 2;
1662 auto& block_for_row = block;
1664 row.
wires = { y_p, x_0, y_0, z_p };
1665 row.q_m = -addconstp;
1666 row.q_c = -addconst0 * linear_term_scale_factor;
1667 row.q_2 = -x_mulconst0 * linear_term_scale_factor;
1668 row.q_3 = y_mulconst0 * linear_term_scale_factor;
1671 block_for_row.append_gate(row);
1675 auto& block_for_row = block;
1677 row.
wires = { x_p, x_1, y_1, z_0 };
1678 row.q_c = -addconst1;
1679 row.q_2 = -x_mulconst1;
1680 row.q_3 = y_mulconst1;
1683 block_for_row.append_gate(row);
1687 auto& block_for_row = block;
1689 row.
wires = { x_2, y_2, z_2, z_1 };
1690 row.q_c = -addconst2;
1691 row.q_1 = -x_mulconst2;
1692 row.q_2 = y_mulconst2;
1696 block_for_row.append_gate(row);
1700 auto& block_for_row = block;
1702 row.
wires = { x_3, y_3, z_3, this->zero_idx() };
1703 row.q_c = -addconst3;
1704 row.q_1 = -x_mulconst3;
1705 row.q_2 = y_mulconst3;
1709 block_for_row.append_gate(row);
1712 this->increment_num_gates(4);
1714 z_0, z_1, z_2, z_3, z_p,
1727template <
typename ExecutionTrace>
1730 return this->rom_ram_logic.create_ROM_array(array_size);
1742template <
typename ExecutionTrace>
1745 return this->rom_ram_logic.create_RAM_array(array_size);
1755template <
typename ExecutionTrace>
1757 const size_t index_value,
1758 const uint32_t value_witness)
1760 this->rom_ram_logic.init_RAM_element(
this, ram_id, index_value, value_witness);
1763template <
typename ExecutionTrace>
1766 return this->rom_ram_logic.read_RAM_array(
this, ram_id, index_witness);
1769template <
typename ExecutionTrace>
1771 const uint32_t index_witness,
1772 const uint32_t value_witness)
1774 this->rom_ram_logic.write_RAM_array(
this, ram_id, index_witness, value_witness);
1792template <
typename ExecutionTrace>
1794 const size_t index_value,
1795 const uint32_t value_witness)
1797 this->rom_ram_logic.set_ROM_element(
this, rom_id, index_value, value_witness);
1807template <
typename ExecutionTrace>
1809 const size_t index_value,
1810 const std::array<uint32_t, 2>& value_witnesses)
1812 this->rom_ram_logic.set_ROM_element_pair(
this, rom_id, index_value, value_witnesses);
1822template <
typename ExecutionTrace>
1825 return this->rom_ram_logic.read_ROM_array(
this, rom_id, index_witness);
1835template <
typename ExecutionTrace>
1837 const uint32_t index_witness)
1839 return this->rom_ram_logic.read_ROM_array_pair(
this, rom_id, index_witness);
1847template <
typename FF>
1850 if constexpr (
requires { this->blocks.poseidon2_external; }) {
1851 auto& block = this->blocks.poseidon2_external;
1852 block.append_gate({ .wires = { in.
a, in.
b, in.
c, in.
d },
1859 this->increment_num_gates();
1861 throw_or_abort(
"create_poseidon2_external_gate base is Ultra-only (Mega overrides into its poseidon2 block)");
1869template <
typename FF>
1872 if constexpr (
requires { this->blocks.poseidon2_internal; }) {
1873 auto& block = this->blocks.poseidon2_internal;
1875 auto& block_for_row = block;
1881 block_for_row.append_gate(row);
1883 this->increment_num_gates();
1885 throw_or_abort(
"create_poseidon2_internal_gate is Ultra-only (Mega uses the compressed block)");
1899 auto first_zero_idx = this->get_first_variable_in_class(this->zero_idx());
1900 if (!this->variable_names.contains(first_zero_idx)) {
1901 this->set_variable_name(this->zero_idx(),
"zero");
1903 this->variable_names[first_zero_idx] =
"zero";
1909 FF::Params::modulus_0, FF::Params::modulus_1, FF::Params::modulus_2, FF::Params::modulus_3
1911 std::stringstream buf;
1913 << modulus[1] <<
std::setw(16) << modulus[0];
1917 for (uint32_t i = 0; i < this->num_public_inputs(); i++) {
1918 cir.
public_inps.push_back(this->real_variable_index[this->public_inputs()[i]]);
1921 for (
auto& tup : base::variable_names) {
1922 cir.
vars_of_interest.insert({ this->real_variable_index[tup.first], tup.second });
1925 for (
const auto& var : this->get_variables()) {
1938 for (
auto& block : blocks.get()) {
1941 for (
size_t idx = 0; idx < block.size(); ++idx) {
1942 std::vector<FF> tmp_sel = { block.q_m()[idx],
1956 std::vector<uint32_t> tmp_w = {
1957 this->real_variable_index[block.w_l()[idx]],
1958 this->real_variable_index[block.w_r()[idx]],
1959 this->real_variable_index[block.w_o()[idx]],
1960 this->real_variable_index[block.w_4()[idx]],
1963 if (idx < block.size() - 1) {
1964 tmp_w.push_back(this->real_variable_index[block.w_l()[idx + 1]]);
1965 tmp_w.push_back(this->real_variable_index[block.w_r()[idx + 1]]);
1966 tmp_w.push_back(this->real_variable_index[block.w_o()[idx + 1]]);
1967 tmp_w.push_back(this->real_variable_index[block.w_4()[idx + 1]]);
1975 block_selectors.push_back(tmp_sel);
1976 block_wires.push_back(tmp_w);
1978 cir.
selectors.push_back(block_selectors);
1979 cir.
wires.push_back(block_wires);
1984 for (
const auto& table : this->lookup_tables) {
1987 for (
size_t i = 0; i < table.
size(); ++i) {
1995 for (
const auto& list : range_lists) {
1996 cir.
range_tags[list.second.range_tag] = list.first;
1999 for (
auto& rom_table : this->rom_ram_logic.rom_arrays) {
2000 std::sort(rom_table.records.begin(), rom_table.records.end());
2003 table.reserve(rom_table.records.size());
2004 for (
const auto& rom_entry : rom_table.records) {
2006 this->real_variable_index[rom_entry.index_witness],
2007 this->real_variable_index[rom_entry.value_column1_witness],
2008 this->real_variable_index[rom_entry.value_column2_witness],
2015 for (
auto& ram_table : this->rom_ram_logic.ram_arrays) {
2016 std::sort(ram_table.records.begin(), ram_table.records.end());
2019 table.reserve(ram_table.records.size());
2020 for (
const auto& ram_entry : ram_table.records) {
2021 table.push_back({ this->real_variable_index[ram_entry.index_witness],
2022 this->real_variable_index[ram_entry.value_witness],
2023 this->real_variable_index[ram_entry.timestamp_witness],
2024 ram_entry.access_type });
2033 msgpack::pack(
buffer, cir);
#define BB_ASSERT(expression,...)
#define BB_ASSERT_GTE(left, right,...)
#define BB_ASSERT_GT(left, right,...)
#define BB_ASSERT_EQ(actual, expected,...)
#define BB_ASSERT_LTE(left, right,...)
bb::field< bb::Bn254FrParams > FF
#define BB_BENCH_NAME(name)
void fix_witness(const uint32_t witness_index, const FF &witness_value)
Add a gate equating a particular witness to a constant, fixing its value.
void init_RAM_element(const size_t ram_id, const size_t index_value, const uint32_t value_witness)
Initialize a RAM cell to equal value_witness
void create_ecc_dbl_gate(const ecc_dbl_gate_< FF > &in)
Create an elliptic curve doubling gate.
void create_sort_constraint_with_edges(const std::vector< uint32_t > &variable_indices, const FF &start, const FF &end)
Constrain consecutive variable differences to be in {0, 1, 2, 3}, with boundary checks.
msgpack::sbuffer export_circuit()
void process_range_list(RangeList &list)
void create_poseidon2_internal_gate(const poseidon2_internal_gate_< FF > &in)
Poseidon2 internal round gate, activates the q_poseidon2_internal selector and relation....
std::vector< uint32_t > create_limbed_range_constraint(const uint32_t variable_index, const uint64_t num_bits, const uint64_t target_range_bitnum=DEFAULT_PLOOKUP_RANGE_BITNUM, std::string_view msg="create_limbed_range_constraint")
Range-constrain a variable to [0, 2^num_bits - 1] by decomposing into smaller limbs.
size_t create_RAM_array(const size_t array_size)
Create a new updatable memory region.
void create_small_range_constraint(const uint32_t variable_index, const uint64_t target_range, std::string_view msg="create_small_range_constraint")
Range-constraints for small ranges, where the upper bound (target_range) need not be dyadic....
void create_big_mul_add_gate(const mul_quad_< FF > &in, const bool use_next_gate_w_4=false)
Create a big multiplication-addition gate, where in.a * in.b * in.mul_scaling + in....
void process_range_lists()
std::tuple< scaled_witness, scaled_witness, FF > add_simple
uint32_t read_RAM_array(const size_t ram_id, const uint32_t index_witness)
void create_unconstrained_gates(const std::vector< uint32_t > &variable_index)
void create_add_gate(const add_triple_< FF > &in)
Create an addition gate, where in.a * in.a_scaling + in.b * in.b_scaling + in.c * in....
void create_big_add_gate(const add_quad_< FF > &in, const bool use_next_gate_w_4=false)
Create a big addition gate, where in.a * in.a_scaling + in.b * in.b_scaling + in.c * in....
void create_ecc_add_gate(const ecc_add_gate_ &in)
Create an elliptic curve addition gate.
GateRowT memory_selectors_row(const MEMORY_SELECTORS type) const
Enable the memory gate of particular type.
plookup::BasicTable * register_basic_lookup_table(plookup::BasicTable &&table)
Register a BasicTable with the builder, assigning it a unique table_index.
GateRowT nnf_selectors_row(const NNF_SELECTORS type) const
Enable the nnf gate of particular type.
typename ExecutionTrace::FF FF
std::array< uint32_t, 5 > evaluate_non_native_field_addition(add_simple limb0, add_simple limb1, add_simple limb2, add_simple limb3, std::tuple< uint32_t, uint32_t, FF > limbp)
Construct gates for non-native field addition.
size_t create_ROM_array(const size_t array_size)
Create a new read-only memory region (a.k.a. ROM table)
plookup::ReadData< uint32_t > create_gates_from_plookup_accumulators(const plookup::MultiTableId &id, const plookup::ReadData< FF > &read_values, const uint32_t key_a_index, std::optional< uint32_t > key_b_index=std::nullopt)
Create gates from pre-computed accumulator values which simultaneously establish individual basic-tab...
plookup::BasicTable & get_table(const plookup::BasicTableId id)
Get the basic table with provided ID from the set of tables for the present circuit; create it if it ...
void create_poseidon2_external_gate(const poseidon2_external_gate_< FF > &in)
Poseidon2 external round gate, activates the q_poseidon2_external selector and relation....
std::array< uint32_t, 2 > evaluate_non_native_field_multiplication(const non_native_multiplication_witnesses< FF > &input)
Create gates for a full non-native field multiplication identity a * b = q * p + r.
void populate_public_inputs_block()
Copy the public input idx data into the public inputs trace block.
uint32_t read_ROM_array(const size_t rom_id, const uint32_t index_witness)
Read a single element from ROM.
RangeList create_range_list(const uint64_t target_range)
uint32_t put_constant_variable(const FF &variable)
void set_ROM_element(const size_t rom_id, const size_t index_value, const uint32_t value_witness)
Initialize a rom cell to equal value_witness
void enforce_small_deltas(const std::vector< uint32_t > &variable_indices)
Check for a sequence of variables that the neighboring differences are in {0, 1, 2,...
void create_bool_gate(const uint32_t a)
Generate an arithmetic gate equivalent to x^2 - x = 0, which forces x to be 0 or 1.
void range_constrain_two_limbs(const uint32_t lo_idx, const uint32_t hi_idx, const size_t lo_limb_bits=DEFAULT_NON_NATIVE_FIELD_LIMB_BITS, const size_t hi_limb_bits=DEFAULT_NON_NATIVE_FIELD_LIMB_BITS, std::string_view msg="range_constrain_two_limbs")
void write_RAM_array(const size_t ram_id, const uint32_t index_witness, const uint32_t value_witness)
void set_ROM_element_pair(const size_t rom_id, const size_t index_value, const std::array< uint32_t, 2 > &value_witnesses)
Initialize a ROM array element with a pair of witness values.
std::array< uint32_t, 2 > read_ROM_array_pair(const size_t rom_id, const uint32_t index_witness)
Read a pair of elements from ROM.
std::array< uint32_t, 2 > queue_partial_non_native_field_multiplication(const non_native_partial_multiplication_witnesses< FF > &input)
Queue the addition of gates constraining the limb-multiplication part of a non native field mul.
std::array< uint32_t, 5 > evaluate_non_native_field_subtraction(add_simple limb0, add_simple limb1, add_simple limb2, add_simple limb3, std::tuple< uint32_t, uint32_t, FF > limbp)
Construct gates for non-native field subtraction.
void process_non_native_field_multiplications()
Iterates over the cached_non_native_field_multiplication objects, removes duplicates,...
void create_bilinear_batched_eq_gate(const bilinear_batched_eq_gate_< FF > &in)
Create a bilinear / batched-eq gate.
void create_arithmetic_gate(const arithmetic_triple_< FF > &in)
A plonk gate with disabled (set to zero) fourth wire. q_m * a * b + q_1 * a + q_2 * b + q_3.
void create_lookup_gate(uint32_t key_idx, uint32_t val1_idx, uint32_t val2_idx, plookup::BasicTable &table, const plookup::BasicTable::LookupEntry &entry, FF column_1_step_size=0, FF column_2_step_size=0, FF column_3_step_size=0)
Create a single plookup lookup gate.
static constexpr Fq curve_b
constexpr uint64_t get_msb() const
Container for lookup accumulator values and table reads.
std::vector< BasicTable::LookupEntry > lookup_entries
std::unique_ptr< uint8_t[]> buffer
AffineElement * accumulator
BasicTable create_basic_table(const BasicTableId id, const size_t index)
const MultiTable & get_multitable(const MultiTableId id)
Return the multitable with the provided ID; construct all MultiTables if not constructed already.
Entry point for Barretenberg command-line interface.
field< Bn254FrParams > fr
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...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Serialized state of a circuit.
std::vector< std::vector< std::vector< FF > > > selectors
std::vector< uint32_t > real_variable_index
std::unordered_map< uint32_t, uint64_t > range_tags
std::unordered_map< uint32_t, std::string > vars_of_interest
std::vector< std::vector< uint32_t > > ram_states
std::vector< std::vector< std::array< uint32_t, 2 > > > rom_states
std::vector< std::vector< std::vector< uint32_t > > > ram_records
std::vector< std::vector< std::vector< uint32_t > > > rom_records
std::vector< std::vector< std::vector< FF > > > lookup_tables
std::vector< uint32_t > real_variable_tags
std::vector< uint32_t > public_inps
std::vector< FF > variables
std::vector< std::vector< std::vector< uint32_t > > > wires
One gate: its wire indices, the non-gate selectors present on every block (see NON_GATE_SELECTORS),...
std::array< uint32_t, NUM_WIRES > wires
std::vector< uint32_t > variable_indices
Used to store instructions to create partial_non_native_field_multiplication gates.
std::array< uint32_t, 4 > a
BilinearBatchedEqMode mode
static constexpr std::array< std::array< FF, t >, rounds_f+rounds_p > round_constants
static constexpr uint256_t modulus
std::array< uint32_t, 4 > a
std::array< uint32_t, 4 > q
std::array< uint32_t, 4 > b
std::array< uint32_t, 4 > r
std::array< FF, 4 > neg_modulus
std::array< uint32_t, 4 > b
std::array< uint32_t, 4 > a
A basic table from which we can perform lookups (for example, an xor table)
std::vector< LookupEntry > lookup_gates
std::vector< bb::fr > column_3
std::vector< bb::fr > column_2
std::vector< bb::fr > column_1
void throw_or_abort(std::string const &err)