Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bitwise_trace.cpp
Go to the documentation of this file.
2
3#include <cstddef>
4#include <cstdint>
5#include <memory>
6#include <ranges>
7#include <stdexcept>
8
14
15namespace bb::avm2::tracegen {
16
18 TraceContainer& trace)
19{
20 using C = Column;
21 // Important to not set last to 1 in the first row if there are no events. Otherwise, the skippable condition
22 // would be skipped wrongly as the sub-relation last * (1 - last) = 0 cannot be satisified (after the
23 // randomization process happening in the sumcheck protocol).
24 if (!events.empty()) {
25 // We activate last selector in the first row.
26 trace.set(C::bitwise_last, 0, 1);
27 }
28
29 // Precomputed inverses ranges from 0 to 16. (for columns bitwise_ctr_inv, bitwise_ctr_min_one_inv)
30 std::array<FF, 17> precomputed_inverses = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
31 FF::batch_invert(precomputed_inverses);
32
33 // Lambda to map the column selector of the op_id.
34 const auto get_op_id_column_selector = [](BitwiseOperation op_id) {
35 switch (op_id) {
37 return C::bitwise_sel_and;
39 return C::bitwise_sel_or;
41 return C::bitwise_sel_xor;
42 default:
43 __builtin_unreachable();
44 }
45 };
46
47 uint32_t row = 1;
48 for (const auto& event : events) {
49 auto tag = event.a.get_tag();
50
51 // We start with full inputs and output and we shift
52 // them byte-per-byte to the right.
53 uint128_t input_a = static_cast<uint128_t>(event.a.as_ff());
54 uint128_t input_b = static_cast<uint128_t>(event.b.as_ff());
55 uint128_t output_c = event.res;
56
57 // Error Handling, check tag a is FF or tag a != tag b
58 bool is_tag_ff = event.a.get_tag() == MemoryTag::FF;
59 bool is_tag_mismatch = event.a.get_tag() != event.b.get_tag();
60 // For tag_a != FF
61 // Rely below on MemoryTag::FF being 0
62 static_assert(static_cast<uint8_t>(MemoryTag::FF) == 0);
63 const uint8_t tag_a_u8 = static_cast<uint8_t>(event.a.get_tag());
64 const uint8_t tag_b_u8 = static_cast<uint8_t>(event.b.get_tag());
65
66 FF tag_a_inv = precomputed_inverses[tag_a_u8];
67 // For tag_a != tag_b
68 FF tag_ab_diff_inv = 0;
69 if (tag_a_u8 > tag_b_u8) {
70 tag_ab_diff_inv = precomputed_inverses[tag_a_u8 - tag_b_u8];
71 } else {
72 // (-x)^(-1) = -x^(-1) for a field element x.
73 tag_ab_diff_inv = -precomputed_inverses[tag_b_u8 - tag_a_u8];
74 }
75
76 if (is_tag_ff || is_tag_mismatch) {
77 // There is an error, fill in values that are still needed to satisfy constraints despite the error.
78 trace.set(row,
79 { {
80 { C::bitwise_op_id, static_cast<uint8_t>(event.operation) },
81 { C::bitwise_start, 1 },
82 { C::bitwise_sel_get_ctr, 0 },
83 { C::bitwise_last, 1 }, // Error triggers a last
84 { C::bitwise_acc_ia, event.a.as_ff() },
85 { C::bitwise_acc_ib, event.b.as_ff() },
86 { C::bitwise_acc_ic, output_c },
87 { C::bitwise_ia_byte, event.a.as_ff() },
88 { C::bitwise_ib_byte, event.b.as_ff() },
89 { C::bitwise_ic_byte, output_c },
90 { C::bitwise_tag_a, tag_a_u8 },
91 { C::bitwise_tag_b, tag_b_u8 },
92 { C::bitwise_tag_c, static_cast<uint8_t>(MemoryTag::FF) }, // Since error
93 // Err Flags
94 { C::bitwise_sel_tag_ff_err, is_tag_ff ? 1 : 0 },
95 { C::bitwise_sel_tag_mismatch_err, is_tag_mismatch ? 1 : 0 },
96 { C::bitwise_err, 1 },
97 // Err Helpers
98 { C::bitwise_tag_a_inv, tag_a_inv },
99 { C::bitwise_tag_ab_diff_inv, tag_ab_diff_inv },
100
101 } });
102 row++;
103 continue; // Skip the rest of the processing for this event
104 }
105
106 // At this point we know that we will not error, so we can proceed with the bitwise operation.
107
108 // Note that for tag U1, we take only one bit. This is correctly
109 // captured below since input_a/b and output_c are each a single bit
110 // and the byte mask correctly extracts it.
111 const uint128_t mask_low_byte = (1 << 8) - 1;
112 const auto start_ctr = get_tag_bytes(tag);
113
114 for (int ctr = start_ctr; ctr > 0; ctr--) {
115 bool is_start = (ctr == start_ctr);
116 uint8_t ia_byte = input_a & mask_low_byte;
117 uint8_t ib_byte = input_b & mask_low_byte;
118 trace.set(row,
119 { { { C::bitwise_sel, 1 },
120 { get_op_id_column_selector(event.operation), 1 },
121 { C::bitwise_op_id, static_cast<uint8_t>(event.operation) },
122 // It is fine to use the truncated input_a/b here instead of event.a/b because if event.a/b
123 // were FF values we would have taken the error branch above.
124 { C::bitwise_acc_ia, input_a },
125 { C::bitwise_acc_ib, input_b },
126 { C::bitwise_acc_ic, output_c },
127 { C::bitwise_ia_byte, ia_byte },
128 { C::bitwise_ib_byte, ib_byte },
129 { C::bitwise_ic_byte, output_c & mask_low_byte },
130 { C::bitwise_output_and, ia_byte & ib_byte },
131 { C::bitwise_output_or, ia_byte | ib_byte },
132 { C::bitwise_output_xor, ia_byte ^ ib_byte },
133 { C::bitwise_tag_a, is_start ? tag_a_u8 : 0 },
134 { C::bitwise_tag_b, is_start ? tag_b_u8 : 0 },
135 { C::bitwise_tag_c, is_start ? tag_a_u8 : 0 }, // same as tag_a
136 { C::bitwise_ctr, ctr },
137 { C::bitwise_ctr_inv, precomputed_inverses[static_cast<uint8_t>(ctr)] },
138 { C::bitwise_ctr_min_one_inv, precomputed_inverses[static_cast<uint8_t>(ctr - 1)] },
139 { C::bitwise_last, ctr == 1 ? 1 : 0 },
140 { C::bitwise_start, is_start ? 1 : 0 },
141 { C::bitwise_sel_get_ctr, is_start ? 1 : 0 }, // Same as bitwise_start but in non-error case
142 // Err Helpers, in the happy path we still need to prove we would not have errored
143 { C::bitwise_tag_a_inv, is_start ? tag_a_inv : 0 },
144 { C::bitwise_tag_ab_diff_inv, is_start ? tag_ab_diff_inv : 0 } } });
145
146 input_a >>= 8;
147 input_b >>= 8;
148 output_c >>= 8;
149 row++;
150 }
151 }
152}
153
157 .add<lookup_bitwise_integral_tag_length_settings, InteractionType::LookupIntoIndexedByRow>();
158
159} // namespace bb::avm2::tracegen
void process(const simulation::EventEmitterInterface< simulation::BitwiseEvent >::Container &events, TraceContainer &trace)
static const InteractionDefinition interactions
InteractionDefinition & add(auto &&... args)
TestTraceContainer trace
AvmFlavorSettings::FF FF
Definition field.hpp:10
lookup_settings< lookup_bitwise_byte_operations_settings_ > lookup_bitwise_byte_operations_settings
uint8_t get_tag_bytes(ValueTag tag)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
simulation::PublicDataTreeReadWriteEvent event
unsigned __int128 uint128_t
Definition serialize.hpp:45
static void batch_invert(C &coeffs) noexcept
Batch invert a collection of field elements using Montgomery's trick.