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