Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_circuit.cpp
Go to the documentation of this file.
1#include "ultra_circuit.hpp"
3
4namespace smt_circuit {
5
14 Solver* solver,
15 TermType type,
16 const std::string& tag,
17 bool optimizations,
18 bool rom_ram_relaxed)
19 : CircuitBase(circuit_info.vars_of_interest,
20 circuit_info.variables,
21 circuit_info.public_inps,
22 circuit_info.real_variable_index,
23 circuit_info.real_variable_tags,
24 solver,
25 type,
26 tag,
27 optimizations)
28 , selectors(circuit_info.selectors)
29 , wires_idxs(circuit_info.wires)
30 , lookup_tables(circuit_info.lookup_tables)
31 , range_tags(circuit_info.range_tags)
32 , rom_records(circuit_info.rom_records)
33 , rom_states(circuit_info.rom_states)
34 , ram_records(circuit_info.ram_records)
35 , ram_states(circuit_info.ram_states)
36 , rom_ram_relaxed(rom_ram_relaxed)
37{
38 // Perform all relaxations for gates or
39 // add gate in its normal state to solver
40
41 size_t arith_cursor = 0;
42 while (arith_cursor < this->selectors[BlockType::ARITHMETIC].size()) {
43 arith_cursor = this->handle_arithmetic_relation(arith_cursor);
44 }
45
46 size_t elliptic_cursor = 0;
47 while (elliptic_cursor < this->selectors[BlockType::ELLIPTIC].size()) {
48 elliptic_cursor = this->handle_elliptic_relation(elliptic_cursor);
49 }
50
51 size_t lookup_cursor = 0;
52 while (lookup_cursor < this->selectors[BlockType::LOOKUP].size()) {
53 lookup_cursor = this->handle_lookup_relation(lookup_cursor);
54 }
55
56 size_t nnf_cursor = 0;
57 while (nnf_cursor < this->selectors[BlockType::NNF].size()) {
58 nnf_cursor = this->handle_nnf_relation(nnf_cursor);
59 }
60 this->handle_rom_tables();
61 this->handle_ram_tables();
62
63 // size_t delta_range_cursor = 0;
64 // while(delta_range_cursor < this->selectors[3].size()){
65 // delta_range_cursor = this->handle_delta_range_relation(delta_range_cursor, 3);
66 // }
68
69 info("Finished solver prep");
70}
71
81{
89
90 uint32_t w_l_idx = this->wires_idxs[BlockType::ARITHMETIC][cursor][WireType::w_l];
91 uint32_t w_r_idx = this->wires_idxs[BlockType::ARITHMETIC][cursor][WireType::w_r];
92 uint32_t w_o_idx = this->wires_idxs[BlockType::ARITHMETIC][cursor][WireType::w_o];
93 uint32_t w_4_idx = this->wires_idxs[BlockType::ARITHMETIC][cursor][WireType::w_4];
94 uint32_t w_l_shift_idx = this->wires_idxs[BlockType::ARITHMETIC][cursor][WireType::w_l_shift];
95 uint32_t w_4_shift_idx = this->wires_idxs[BlockType::ARITHMETIC][cursor][WireType::w_4_shift];
96
97 STerm w_l = this->symbolic_vars[w_l_idx];
98 STerm w_r = this->symbolic_vars[w_r_idx];
99 STerm w_o = this->symbolic_vars[w_o_idx];
100 STerm w_4 = this->symbolic_vars[w_4_idx];
101 STerm w_4_shift = this->symbolic_vars[w_4_shift_idx];
102 STerm w_l_shift = this->symbolic_vars[w_l_shift_idx];
103
104 std::vector<bb::fr> this_gate = { q_m, q_l, q_r, q_o, q_c, q_arith, q_4 };
105
106 std::vector<bb::fr> boolean_gate = { 1, -1, 0, 0, 0, 1, 0 };
107 bool boolean_gate_flag = (boolean_gate == this_gate) && (w_l_idx == w_r_idx) && (w_o_idx == 0) && (w_4_idx == 0);
108 if (boolean_gate_flag) {
109 (Bool(w_l) == Bool(STerm(0, this->solver, this->type)) | Bool(w_l) == Bool(STerm(1, this->solver, this->type)))
110 .assert_term();
111 return cursor + 1;
112 }
113
114 std::vector<bb::fr> fix_witness_gate = { 0, 1, 0, 0, q_c, 1, 0 };
115 bool put_constant_variable_flag =
116 (fix_witness_gate == this_gate) && (w_r_idx == 0) && (w_o_idx == 0) && (w_4_idx == 0);
117 if (put_constant_variable_flag) {
118 w_l == -q_c;
119 return cursor + 1;
120 }
121
122 STerm res = this->symbolic_vars[this->variable_names_inverse["zero"]];
123 static const bb::fr neg_half = bb::fr(-2).invert();
124
125 if (!q_arith.is_zero()) {
126 if (q_m != 0) {
127 res += ((q_arith - 3) * q_m * neg_half) * w_r * w_l;
128 }
129 if (q_l != 0) {
130 res += (q_l * w_l);
131 }
132 if (q_r != 0) {
133 res += (q_r * w_r);
134 }
135 if (q_o != 0) {
136 res += (q_o * w_o);
137 }
138 if (q_4 != 0) {
139 res += (q_4 * w_4);
140 }
141 if (q_c != 0) {
142 res += q_c;
143 }
144 if (q_arith != 1) {
145 res += (q_arith - 1) * w_4_shift;
146 }
147 // res *= q_arith;
148 res == bb::fr::zero();
149
150 optimized[w_l_idx] = false;
151 optimized[w_r_idx] = false;
152 optimized[w_o_idx] = false;
153 optimized[w_4_idx] = false;
154 optimized[w_4_shift_idx] = false;
155 }
156
157 if (q_arith * (q_arith - 1) * (q_arith - 2) != 0) {
158 res = w_l + w_4 - w_l_shift + q_m;
159 res == bb::fr::zero();
160 optimized[w_l_shift_idx] = false;
161 }
162
163 return cursor + 1;
164}
165
166void UltraCircuit::process_new_table(uint32_t table_idx)
167{
168 std::vector<STuple> new_table;
169 bool is_xor = true;
170 bool is_and = true;
171
172 for (auto table_entry : this->lookup_tables[table_idx]) {
173 STuple tmp_entry({
174 STerm(table_entry[0], this->solver, this->type),
175 STerm(table_entry[1], this->solver, this->type),
176 STerm(table_entry[2], this->solver, this->type),
177 });
178 new_table.push_back(tmp_entry);
179
180 is_xor &= (static_cast<uint256_t>(table_entry[0]) ^ static_cast<uint256_t>(table_entry[1])) ==
181 static_cast<uint256_t>(table_entry[2]);
182 is_and &= (static_cast<uint256_t>(table_entry[0]) & static_cast<uint256_t>(table_entry[1])) ==
183 static_cast<uint256_t>(table_entry[2]);
184 }
185 info(RED, "Creating lookup table №", this->cached_symbolic_tables.size());
186 std::string table_name;
187 if (is_xor) {
188 table_name = "XOR_TABLE_" + std::to_string(new_table.size());
189 this->tables_types.insert({ table_idx, TableType::XOR });
190 } else if (is_and) {
191 table_name = "AND_TABLE_" + std::to_string(new_table.size());
192 this->tables_types.insert({ table_idx, TableType::AND });
193 } else {
194 table_name = "UNK_TABLE_" + std::to_string(new_table.size());
195 this->tables_types.insert({ table_idx, TableType::UNKNOWN });
196 }
197 this->tables_sizes.insert({ table_idx, new_table.size() });
198
199 info(table_name, RESET);
200 SymSet<STuple> new_stable(new_table, table_name + this->tag);
201 this->cached_symbolic_tables.insert({ table_idx, new_stable });
202}
203
213{
218 bb::fr q_lookup = this->selectors[BlockType::LOOKUP][cursor][SelectorType::q_lookup];
219
220 if (q_lookup.is_zero()) {
221 return cursor + 1;
222 }
223
224 uint32_t w_l_idx = this->wires_idxs[BlockType::LOOKUP][cursor][WireType::w_l];
225 uint32_t w_r_idx = this->wires_idxs[BlockType::LOOKUP][cursor][WireType::w_r];
226 uint32_t w_o_idx = this->wires_idxs[BlockType::LOOKUP][cursor][WireType::w_o];
227 uint32_t w_l_shift_idx = this->wires_idxs[BlockType::LOOKUP][cursor][WireType::w_l_shift];
228 uint32_t w_r_shift_idx = this->wires_idxs[BlockType::LOOKUP][cursor][WireType::w_r_shift];
229 uint32_t w_o_shift_idx = this->wires_idxs[BlockType::LOOKUP][cursor][WireType::w_o_shift];
230
231 optimized[w_l_idx] = false;
232 optimized[w_r_idx] = false;
233 optimized[w_o_idx] = false;
234 optimized[w_l_shift_idx] = false;
235 optimized[w_r_shift_idx] = false;
236 optimized[w_o_shift_idx] = false;
237
238 auto table_idx = static_cast<uint32_t>(q_o);
239 if (!this->cached_symbolic_tables.contains(table_idx)) {
240 this->process_new_table(table_idx);
241 }
242
243 STerm first_entry = this->symbolic_vars[w_l_idx] + q_r * this->symbolic_vars[w_l_shift_idx];
244 STerm second_entry = this->symbolic_vars[w_r_idx] + q_m * this->symbolic_vars[w_r_shift_idx];
245 STerm third_entry = this->symbolic_vars[w_o_idx] + q_c * this->symbolic_vars[w_o_shift_idx];
246
247 if (this->type == TermType::BVTerm && this->enable_optimizations) {
248 // Sort of an optimization.
249 // However if we don't do this, solver will find a unique witness that corresponds to overflowed value.
250 if (q_r == -64 && q_m == -64 && q_c == -64) {
251 this->symbolic_vars[w_l_shift_idx] = this->symbolic_vars[w_l_idx] >> 6;
252 this->symbolic_vars[w_r_shift_idx] = this->symbolic_vars[w_r_idx] >> 6;
253 this->symbolic_vars[w_o_shift_idx] = this->symbolic_vars[w_o_idx] >> 6;
254 }
255
256 auto sqrt = [](size_t table_size) -> size_t {
257 auto [is_sqr, res] = bb::fr(table_size).sqrt();
258 info("Is square: ", is_sqr);
259 if (!(uint256_t(res) < (uint256_t(1) << 32) || uint256_t(-res) < (uint256_t(1) << 32))) {
260 info("bad sqrt");
261 abort();
262 }
263 auto ures = uint256_t(res) > (uint256_t(1) << 32) ? uint256_t(-res) : uint256_t(res);
264 return static_cast<size_t>(ures);
265 };
266
267 switch (this->tables_types[table_idx]) {
268 case TableType::XOR: {
269 info("XOR optimization");
270
271 size_t max_val = sqrt(this->tables_sizes[table_idx]);
272 first_entry < max_val;
273 second_entry < max_val;
274 third_entry < max_val;
275
276 (first_entry ^ second_entry) == third_entry;
277 return cursor + 1;
278 }
279 case TableType::AND: {
280 info("AND optimization");
281
282 size_t max_val = sqrt(this->tables_sizes[table_idx]);
283 first_entry < max_val;
284 second_entry < max_val;
285 third_entry < max_val;
286
287 (first_entry & second_entry) == third_entry;
288 return cursor + 1;
289 }
291 break;
292 }
293 }
294 info("Unknown Table");
295 STuple entries({ first_entry, second_entry, third_entry });
296 this->cached_symbolic_tables[table_idx].contains(entries);
297 return cursor + 1;
298}
299
307{
308 bb::fr q_is_double = this->selectors[BlockType::ELLIPTIC][cursor][SelectorType::q_m];
309 bb::fr q_sign = this->selectors[BlockType::ELLIPTIC][cursor][SelectorType::q_1];
311 if (q_elliptic.is_zero()) {
312 return cursor + 1;
313 }
314
315 uint32_t w_r_idx = this->wires_idxs[BlockType::ELLIPTIC][cursor][WireType::w_r];
316 uint32_t w_o_idx = this->wires_idxs[BlockType::ELLIPTIC][cursor][WireType::w_o];
317 uint32_t w_l_shift_idx = this->wires_idxs[BlockType::ELLIPTIC][cursor][WireType::w_l_shift];
318 uint32_t w_r_shift_idx = this->wires_idxs[BlockType::ELLIPTIC][cursor][WireType::w_r_shift];
319 uint32_t w_o_shift_idx = this->wires_idxs[BlockType::ELLIPTIC][cursor][WireType::w_o_shift];
320 uint32_t w_4_shift_idx = this->wires_idxs[BlockType::ELLIPTIC][cursor][WireType::w_4_shift];
321 optimized[w_r_idx] = false;
322 optimized[w_o_idx] = false;
323 optimized[w_l_shift_idx] = false;
324 optimized[w_r_shift_idx] = false;
325 optimized[w_o_shift_idx] = false;
326 optimized[w_4_shift_idx] = false;
327
328 STerm x_1 = this->symbolic_vars[w_r_idx];
329 STerm y_1 = this->symbolic_vars[w_o_idx];
330 STerm x_2 = this->symbolic_vars[w_l_shift_idx];
331 STerm y_2 = this->symbolic_vars[w_4_shift_idx];
332 STerm x_3 = this->symbolic_vars[w_r_shift_idx];
333 STerm y_3 = this->symbolic_vars[w_o_shift_idx];
334
335 auto x_diff = (x_2 - x_1);
336 auto y2_sqr = (y_2 * y_2);
337 auto y1_sqr = (y_1 * y_1);
338 auto y1y2 = y_1 * y_2 * q_sign;
339 auto x_add_identity = (x_3 + x_2 + x_1) * x_diff * x_diff - y2_sqr - y1_sqr + y1y2 + y1y2;
340
341 auto y1_plus_y3 = y_1 + y_3;
342 auto y_diff = y_2 * q_sign - y_1;
343 auto y_add_identity = y1_plus_y3 * x_diff + (x_3 - x_1) * y_diff;
344
345 if (q_is_double.is_zero()) {
346 x_add_identity == 0; // scaling_factor = 1
347 y_add_identity == 0; // scaling_factor = 1
348 }
349
351 auto x_pow_4 = (y1_sqr - curve_b) * x_1;
352 auto y1_sqr_mul_4 = y1_sqr + y1_sqr;
353 y1_sqr_mul_4 += y1_sqr_mul_4;
354 auto x1_pow_4_mul_9 = x_pow_4 * 9;
355 auto x_double_identity = (x_3 + x_1 + x_1) * y1_sqr_mul_4 - x1_pow_4_mul_9;
356
357 auto x1_sqr_mul_3 = (x_1 + x_1 + x_1) * x_1;
358 auto y_double_identity = x1_sqr_mul_3 * (x_1 - x_3) - (y_1 + y_1) * (y_1 + y_3);
359
360 if (!q_is_double.is_zero()) {
361 x_double_identity == 0; // scaling_factor = 1
362 y_double_identity == 0; // scaling_factor = 1
363 }
364
365 return cursor + 1;
366}
367
376{
378 if (q_delta_range == 0) {
379 return cursor + 1;
380 }
381
382 uint32_t w_l_idx = this->wires_idxs[BlockType::DELTA_RANGE][cursor][WireType::w_l];
383 uint32_t w_r_idx = this->wires_idxs[BlockType::DELTA_RANGE][cursor][WireType::w_r];
384 uint32_t w_o_idx = this->wires_idxs[BlockType::DELTA_RANGE][cursor][WireType::w_o];
385 uint32_t w_4_idx = this->wires_idxs[BlockType::DELTA_RANGE][cursor][WireType::w_4];
386 uint32_t w_l_shift_idx = this->wires_idxs[BlockType::DELTA_RANGE][cursor][WireType::w_l_shift];
387
388 STerm w_1 = this->symbolic_vars[w_l_idx];
389 STerm w_2 = this->symbolic_vars[w_r_idx];
390 STerm w_3 = this->symbolic_vars[w_o_idx];
391 STerm w_4 = this->symbolic_vars[w_4_idx];
392 STerm w_1_shift = this->symbolic_vars[w_l_shift_idx];
393
394 STerm delta_1 = w_2 - w_1;
395 STerm delta_2 = w_3 - w_2;
396 STerm delta_3 = w_4 - w_3;
397 STerm delta_4 = w_1_shift - w_4;
398
399 STerm tmp = (delta_1 - 1) * (delta_1 - 1) - 1;
400 tmp *= (delta_1 - 2) * (delta_1 - 2) - 1;
401 tmp == 0;
402
403 tmp = (delta_2 - 1) * (delta_2 - 1) - 1;
404 tmp *= (delta_2 - 2) * (delta_2 - 2) - 1;
405 tmp == 0;
406
407 tmp = (delta_3 - 1) * (delta_3 - 1) - 1;
408 tmp *= (delta_3 - 2) * (delta_3 - 2) - 1;
409 tmp == 0;
410
411 tmp = (delta_4 - 1) * (delta_4 - 1) - 1;
412 tmp *= (delta_4 - 2) * (delta_4 - 2) - 1;
413 tmp == 0;
414
415 return cursor + 1;
416}
417
422{
423 for (uint32_t i = 0; i < this->get_num_vars(); i++) {
424 if (i != this->real_variable_index[i] || optimized[i]) {
425 continue;
426 }
427
428 uint32_t tag = this->real_variable_tags[i];
429 if (tag != 0 && this->range_tags.contains(tag)) {
430 uint64_t range = this->range_tags[tag];
431 if (this->type == TermType::FFTerm || !this->enable_optimizations) {
432 if (!this->cached_range_tables.contains(range)) {
433 std::vector<STerm> new_range_table;
434 for (size_t entry = 0; entry <= range; entry++) {
435 new_range_table.push_back(STerm(entry, this->solver, this->type));
436 }
437 std::string table_name = "RANGE_" + std::to_string(range) + this->tag;
438 SymSet<STerm> new_range_stable(new_range_table, table_name);
439 info(RED, "Initialized new range: ", table_name, RESET);
440 this->cached_range_tables.insert({ range, new_range_stable });
441 }
442 this->cached_range_tables[range].contains(this->symbolic_vars[i]);
443 } else {
444 this->symbolic_vars[i] <= range;
445 }
446 optimized[i] = false;
447 }
448 }
449}
450
459{
460 bb::fr q_nnf = this->selectors[BlockType::NNF][cursor][SelectorType::q_nnf];
461 if (q_nnf == 0) {
462 return cursor + 1;
463 }
464
465 uint32_t w_l_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_l];
466 uint32_t w_r_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_r];
467 uint32_t w_o_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_o];
468 uint32_t w_4_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_4];
469 uint32_t w_l_shift_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_l_shift];
470 uint32_t w_r_shift_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_r_shift];
471 uint32_t w_o_shift_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_o_shift];
472 uint32_t w_4_shift_idx = this->wires_idxs[BlockType::NNF][cursor][WireType::w_4_shift];
473
474 STerm w_1 = this->symbolic_vars[w_l_idx];
475 STerm w_2 = this->symbolic_vars[w_r_idx];
476 STerm w_3 = this->symbolic_vars[w_o_idx];
477 STerm w_4 = this->symbolic_vars[w_4_idx];
478 STerm w_1_shift = this->symbolic_vars[w_l_shift_idx];
479 STerm w_2_shift = this->symbolic_vars[w_r_shift_idx];
480 STerm w_3_shift = this->symbolic_vars[w_o_shift_idx];
481 STerm w_4_shift = this->symbolic_vars[w_4_shift_idx];
482
483 bb::fr q_m = this->selectors[BlockType::NNF][cursor][SelectorType::q_m];
484 bb::fr q_2 = this->selectors[BlockType::NNF][cursor][SelectorType::q_2];
485 bb::fr q_3 = this->selectors[BlockType::NNF][cursor][SelectorType::q_3];
486 bb::fr q_4 = this->selectors[BlockType::NNF][cursor][SelectorType::q_4];
487
488 bb::fr LIMB_SIZE(uint256_t(1) << 68);
489 bb::fr SUBLIMB_SHIFT(uint256_t(1) << 14);
490
491 // reassure that only one entry
492 size_t entry_flag = 0;
493
494 if (q_3 != 0 && q_4 != 0) {
495 info("BF 1");
496 entry_flag += 1;
497 // BigField Limb Accumulation 1
498 STerm limb_accumulator_1 = w_2_shift * SUBLIMB_SHIFT;
499 limb_accumulator_1 += w_1_shift;
500 limb_accumulator_1 *= SUBLIMB_SHIFT;
501 limb_accumulator_1 += w_3;
502 limb_accumulator_1 *= SUBLIMB_SHIFT;
503 limb_accumulator_1 += w_2;
504 limb_accumulator_1 *= SUBLIMB_SHIFT;
505 limb_accumulator_1 += w_1;
506 limb_accumulator_1 -= w_4;
507 limb_accumulator_1 == 0;
508 }
509
510 if (q_3 != 0 && q_m != 0) {
511 info("BF 2");
512 entry_flag += 1;
513 // BigField Limb Accumulation 2
514 STerm limb_accumulator_2 = w_3_shift * SUBLIMB_SHIFT;
515 limb_accumulator_2 += w_2_shift;
516 limb_accumulator_2 *= SUBLIMB_SHIFT;
517 limb_accumulator_2 += w_1_shift;
518 limb_accumulator_2 *= SUBLIMB_SHIFT;
519 limb_accumulator_2 += w_4;
520 limb_accumulator_2 *= SUBLIMB_SHIFT;
521 limb_accumulator_2 += w_3;
522 limb_accumulator_2 -= w_4_shift;
523 limb_accumulator_2 == 0;
524 }
525
526 STerm limb_subproduct = w_1 * w_2_shift + w_1_shift * w_2;
527 if (q_2 != 0 && q_4 != 0) {
528 info("BF pr 2");
529 entry_flag += 1;
530 // BigField Product 2
531 STerm non_native_field_gate_2 = (w_1 * w_4 + w_2 * w_3 - w_3_shift);
532 non_native_field_gate_2 *= LIMB_SIZE;
533 non_native_field_gate_2 -= w_4_shift;
534 non_native_field_gate_2 += limb_subproduct;
535 non_native_field_gate_2 == 0;
536 }
537
538 limb_subproduct *= LIMB_SIZE;
539 limb_subproduct += (w_1_shift * w_2_shift);
540 if (q_2 != 0 && q_3 != 0) {
541 info("BF pr 1");
542 entry_flag += 1;
543 // BigField Product 1
544 STerm non_native_field_gate_1 = limb_subproduct;
545 non_native_field_gate_1 -= (w_3 + w_4);
546 non_native_field_gate_1 == 0;
547 }
548
549 if (q_2 != 0 && q_m != 0) {
550 info("BF pr 3");
551 entry_flag += 1;
552 // BigField Product 3
553 STerm non_native_field_gate_3 = limb_subproduct;
554 non_native_field_gate_3 += w_4;
555 non_native_field_gate_3 -= (w_3_shift + w_4_shift);
556 non_native_field_gate_3 == 0;
557 }
558
559 if (entry_flag > 1) {
560 throw std::runtime_error("Double entry in NNF");
561 }
562 return cursor + 1;
563}
564
573void UltraCircuit::rom_table_read(uint32_t rom_array_idx,
574 uint32_t rom_index_idx,
575 uint32_t read_to_value1_idx,
576 uint32_t read_to_value2_idx)
577{
578 if (this->public_inps.contains(rom_index_idx) || this->rom_ram_relaxed) {
579 STerm index = this->symbolic_vars[rom_index_idx];
580 index == this->variables[rom_index_idx];
581 }
582
583 SymArray<STerm, STuple> rom_table = this->cached_rom_tables[rom_array_idx];
584 STerm index = this->symbolic_vars[rom_index_idx];
585 STuple table_entry = rom_table[index]; // <- symbolic read
586
587 STerm value1 = this->symbolic_vars[read_to_value1_idx];
588 STerm value2 = this->symbolic_vars[read_to_value2_idx];
589 STuple value_entry({ value1, value2 });
590
591 table_entry == value_entry;
592}
593
601void UltraCircuit::ram_table_read(uint32_t ram_array_idx, uint32_t ram_index_idx, uint32_t read_to_value_idx)
602{
603 if (this->public_inps.contains(ram_index_idx) || this->rom_ram_relaxed) {
604 STerm index = this->symbolic_vars[ram_index_idx];
605 index == this->variables[ram_index_idx];
606 }
607
608 SymArray<STerm, STerm> ram_table = this->cached_ram_tables[ram_array_idx];
609 STerm index = this->symbolic_vars[ram_index_idx];
610 STerm table_entry = ram_table[index]; // <- symbolic read
611
612 STerm value_entry = this->symbolic_vars[read_to_value_idx];
613
614 table_entry == value_entry;
615}
616
624void UltraCircuit::ram_table_write(uint32_t ram_array_idx, uint32_t ram_index_idx, uint32_t read_from_value_idx)
625{
626 if (this->public_inps.contains(ram_index_idx) || this->rom_ram_relaxed) {
627 STerm index = this->symbolic_vars[ram_index_idx];
628 index == this->variables[ram_index_idx];
629 }
630
631 SymArray<STerm, STerm>& ram_table = this->cached_ram_tables[ram_array_idx];
632 STerm index = this->symbolic_vars[ram_index_idx];
633 STerm value_entry = this->symbolic_vars[read_from_value_idx];
634
635 ram_table.put(index, value_entry);
636}
637
643{
644 static constexpr uint32_t UNINITIALIZED_MEMORY_RECORD = UINT32_MAX;
645
646 STerm idx_ex = this->symbolic_vars[this->variable_names_inverse["zero"]];
647 STuple entry_ex({ idx_ex, idx_ex });
648
649 cvc5::Sort ind_sort = idx_ex.term.getSort();
650 TermType ind_type = idx_ex.type;
651 cvc5::Sort entry_sort = entry_ex.term.getSort();
652 TermType entry_type = entry_ex.type;
653
654 for (uint32_t i = 0; i < this->rom_records.size(); i++) {
655 SymArray<STerm, STuple> rom_table(
656 ind_sort, ind_type, entry_sort, entry_type, this->solver, "ROM_TABLE#" + std::to_string(i));
657 // Fill the ROM table
658 for (size_t j = 0; j < this->rom_states[i].size(); j++) {
659 STerm idx(static_cast<bb::fr>(j), this->solver, ind_type);
660 if (this->rom_states[i][j][0] == UNINITIALIZED_MEMORY_RECORD) {
661 continue;
662 }
663
664 STerm value1 = this->symbolic_vars[this->rom_states[i][j][0]];
665 STerm value2 = this->symbolic_vars[this->rom_states[i][j][1]];
666 rom_table.put(idx, STuple({ value1, value2 }));
667 }
668 this->cached_rom_tables.insert({ i, rom_table });
669
670 // process all the reads
671 for (auto rom_record : this->rom_records[i]) {
672 uint32_t index_witness = rom_record[0];
673 uint32_t value1_witness = rom_record[1];
674 uint32_t value2_witness = rom_record[2];
675 this->rom_table_read(i, index_witness, value1_witness, value2_witness);
676 }
677 }
678}
679
685{
686 STerm idx_ex = this->symbolic_vars[this->variable_names_inverse["zero"]];
687 STuple entry_ex({ idx_ex, idx_ex });
688
689 cvc5::Sort sort = idx_ex.term.getSort();
690 TermType type = idx_ex.type;
691
692 for (uint32_t i = 0; i < this->ram_records.size(); i++) {
693 SymArray<STerm, STerm> ram_table(sort, type, sort, type, this->solver, "RAM_TABLE#" + std::to_string(i));
694 this->cached_ram_tables.insert({ i, ram_table });
695
696 // process all the reads and writes
697 for (auto ram_record : this->ram_records[i]) {
698 uint32_t index_witness = ram_record[0];
699 uint32_t value_witness = ram_record[1];
700 // uint32_t timestamp_witness = ram_record[2];
701 uint32_t access_type = ram_record[3];
702 switch (access_type) {
703 case 0:
704 this->ram_table_read(i, index_witness, value_witness);
705 break;
706 case 1:
707 this->ram_table_write(i, index_witness, value_witness);
708 break;
709 default:
710 info("Reached an invalid access type");
711 abort();
712 }
713 }
714 }
715}
716
728{
729 if (witness.size() != this->get_num_vars()) {
730 throw std::invalid_argument("Witness size should be " + std::to_string(this->get_num_vars()) +
731
732 std::to_string(witness.size()));
733 }
734 return true;
735}
736
753 CircuitSchema& circuit_info,
754 Solver* s,
755 TermType type,
756 const std::vector<std::string>& equal,
757 const std::vector<std::string>& not_equal,
758 const std::vector<std::string>& equal_at_the_same_time,
759 const std::vector<std::string>& not_equal_at_the_same_time,
760 bool enable_optimizations)
761{
762 UltraCircuit c1(circuit_info, s, type, "circuit1", enable_optimizations);
763 UltraCircuit c2(circuit_info, s, type, "circuit2", enable_optimizations);
764
765 for (const auto& term : equal) {
766 c1[term] == c2[term];
767 }
768 for (const auto& term : not_equal) {
769 c1[term] != c2[term];
770 }
771
773 for (const auto& term : equal_at_the_same_time) {
774 Bool tmp = Bool(c1[term]) == Bool(c2[term]);
775 eqs.push_back(tmp);
776 }
777
778 if (eqs.size() > 1) {
779 batch_or(eqs).assert_term();
780 } else if (eqs.size() == 1) {
781 eqs[0].assert_term();
782 }
783
785 for (const auto& term : not_equal_at_the_same_time) {
786 Bool tmp = Bool(c1[term]) != Bool(c2[term]);
787 neqs.push_back(tmp);
788 }
789
790 if (neqs.size() > 1) {
791 batch_or(neqs).assert_term();
792 } else if (neqs.size() == 1) {
793 neqs[0].assert_term();
794 }
795 return { c1, c2 };
796}
797
812 Solver* s,
813 TermType type,
814 const std::vector<std::string>& equal,
815 bool enable_optimizations)
816{
817 UltraCircuit c1(circuit_info, s, type, "circuit1", enable_optimizations);
818 UltraCircuit c2(circuit_info, s, type, "circuit2", enable_optimizations);
819
820 for (const auto& term : equal) {
821 c1[term] == c2[term];
822 }
823
825 for (const auto& node : c1.symbolic_vars) {
826 uint32_t i = node.first;
827 if (std::find(equal.begin(), equal.end(), std::string(c1.variable_names[i])) != equal.end()) {
828 continue;
829 }
830 if (c1.optimized[i]) {
831 continue;
832 }
833 Bool tmp = Bool(c1[i]) != Bool(c2[i]);
834 neqs.push_back(tmp);
835 }
836
837 if (neqs.size() > 1) {
838 batch_or(neqs).assert_term();
839 } else if (neqs.size() == 1) {
840 neqs[0].assert_term();
841 }
842 return { c1, c2 };
843}
844}; // namespace smt_circuit
Base class for symbolic circuits.
std::vector< uint32_t > real_variable_index
std::unordered_set< uint32_t > public_inps
std::vector< uint32_t > real_variable_tags
std::unordered_map< uint32_t, bool > optimized
std::vector< bb::fr > variables
size_t get_num_vars() const
std::unordered_map< uint32_t, std::string > variable_names
std::unordered_map< std::string, uint32_t > variable_names_inverse
std::unordered_map< uint32_t, STerm > symbolic_vars
Symbolic Circuit class for Standard Circuit Builder.
std::unordered_map< uint32_t, SymSet< STuple > > cached_symbolic_tables
std::unordered_map< uint32_t, size_t > tables_sizes
UltraCircuit(CircuitSchema &circuit_info, Solver *solver, TermType type=TermType::FFTerm, const std::string &tag="", bool enable_optimizations=true, bool rom_ram_relaxed=false)
Construct a new UltraCircuit object.
void rom_table_read(uint32_t rom_array_idx, uint32_t index_idx, uint32_t value1_idx, uint32_t value2_idx)
Perform read from ROM table.
std::vector< std::vector< std::vector< uint32_t > > > ram_records
std::vector< std::vector< std::vector< bb::fr > > > lookup_tables
std::unordered_map< uint32_t, TableType > tables_types
std::vector< std::vector< std::array< uint32_t, 2 > > > rom_states
std::unordered_map< uint32_t, SymArray< STerm, STerm > > cached_ram_tables
static std::pair< UltraCircuit, UltraCircuit > unique_witness(CircuitSchema &circuit_info, Solver *s, TermType type, const std::vector< std::string > &equal={}, bool enable_optimizations=false)
Check your circuit for witness uniqueness.
size_t handle_lookup_relation(size_t cursor)
Adds all the lookup gate constraints to the solver. Relaxes constraint system for non-ff solver engin...
bool simulate_circuit_eval(std::vector< bb::fr > &witness) const override
Similar functionality to old .check_circuit() method in standard circuit builder.
size_t handle_elliptic_relation(size_t cursor)
Adds all the elliptic gate constraints to the solver.
size_t handle_nnf_relation(size_t cursor)
Adds all the nnf constraints to the solver.
void handle_ram_tables()
Adds all the RAM related constraints into the solver.
static std::pair< UltraCircuit, UltraCircuit > unique_witness_ext(CircuitSchema &circuit_info, Solver *s, TermType type, const std::vector< std::string > &equal={}, const std::vector< std::string > &not_equal={}, const std::vector< std::string > &equal_at_the_same_time={}, const std::vector< std::string > &not_equal_at_the_same_time={}, bool enable_optimizations=false)
Check your circuit for witness uniqueness.
std::vector< std::vector< std::vector< uint32_t > > > rom_records
std::unordered_map< uint32_t, uint64_t > range_tags
void process_new_table(uint32_t table_idx)
void handle_rom_tables()
Adds all the ROM related constraints into the solver.
void ram_table_read(uint32_t ram_array_idx, uint32_t index_idx, uint32_t value_idx)
Perform read from RAM table.
void ram_table_write(uint32_t ram_array_idx, uint32_t ram_index_idx, uint32_t read_from_value_idx)
Perform write to RAM table.
void handle_range_constraints()
Adds all the range constraints to the solver.
size_t handle_arithmetic_relation(size_t cursor)
Adds all the arithmetic gate constraints to the solver. Relaxes constraint system for non-ff solver e...
std::unordered_map< uint32_t, SymArray< STerm, STuple > > cached_rom_tables
size_t handle_delta_range_relation(size_t cursor)
Adds all the delta_range gate constraints to the solver.
std::vector< std::vector< std::vector< bb::fr > > > selectors
std::unordered_map< uint64_t, SymSet< STerm > > cached_range_tables
std::vector< std::vector< std::vector< uint32_t > > > wires_idxs
Class for the solver.
Definition solver.hpp:80
Bool element class.
Definition bool.hpp:14
Symbolic term element class.
Definition term.hpp:114
TermType type
Definition term.hpp:123
cvc5::Term term
Definition term.hpp:121
sym Tuple class
symbolic Array class
void put(const sym_index &ind, const sym_entry &entry)
symbolic Set class
void info(Args... args)
Definition log.hpp:70
field< Bn254FrParams > fr
Definition fr.hpp:174
TermType
Allows to define three types of symbolic terms STerm - Symbolic Variables acting like a Finte Field e...
Definition term.hpp:15
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
constexpr field invert() const noexcept
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
BB_INLINE constexpr bool is_zero() const noexcept
static constexpr field zero()
static const size_t ARITHMETIC
static const size_t NNF
static const size_t DELTA_RANGE
static const size_t LOOKUP
static const size_t ELLIPTIC
static const size_t q_1
static const size_t q_elliptic
static const size_t q_c
static const size_t q_3
static const size_t q_lookup
static const size_t q_nnf
static const size_t q_4
static const size_t q_m
static const size_t curve_b
static const size_t q_arith
static const size_t q_2
static const size_t q_delta_range
static const size_t w_4_shift
static const size_t w_o
static const size_t w_l_shift
static const size_t w_l
static const size_t w_o_shift
static const size_t w_r
static const size_t w_r_shift
static const size_t w_4
Serialized state of a circuit.
#define RED
#define RESET