Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
bool.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#include "bool.hpp"
8#include "../circuit_builders/circuit_builders.hpp"
12
13using namespace bb;
14
15namespace bb::stdlib {
16
20template <typename Builder>
22 : witness_bool(value)
23{}
24
28template <typename Builder>
30 : context(parent_context)
31{}
32
40template <typename Builder>
41bool_t<Builder>::bool_t(const witness_t<Builder>& value, const bool& use_range_constraint)
43{
44 BB_ASSERT((value.witness == bb::fr::zero()) || (value.witness == bb::fr::one()),
45 "bool_t: witness value is not 0 or 1");
46 witness_index = value.witness_index;
47
48 if (use_range_constraint) {
49 // Create a range constraint gate
50 context->create_new_range_constraint(witness_index, 1, "bool_t: witness value is not 0 or 1");
51 } else {
52 // Create an arithmetic gate to enforce the relation x^2 = x
53 context->create_bool_gate(witness_index);
54 }
55 witness_bool = (value.witness == bb::fr::one());
56 witness_inverted = false;
58}
62template <typename Builder>
63bool_t<Builder>::bool_t(Builder* parent_context, const bool value)
64 : context(parent_context)
65 , witness_bool(value)
66{}
67
71template <typename Builder>
74 , witness_bool(other.witness_bool)
75 , witness_inverted(other.witness_inverted)
76 , witness_index(other.witness_index)
77 , tag(other.tag)
78{}
83template <typename Builder>
85 : context(other.context)
86 , witness_bool(other.witness_bool)
87 , witness_inverted(other.witness_inverted)
88 , witness_index(other.witness_index)
89 , tag(other.tag)
90{}
96template <typename Builder>
98{
99 BB_ASSERT(witness_index != IS_CONSTANT);
100 bool_t<Builder> result(ctx);
101 result.witness_index = witness_index;
102 const bb::fr value = ctx->get_variable(witness_index);
103 // It does not create a constraint.
104 BB_ASSERT_EQ(value * value - value, 0, "bool_t: creating a witness bool from a non-boolean value");
105 result.witness_bool = (value == 1);
106 result.witness_inverted = false;
107 return result;
108}
109
113template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool other)
114{
115 context = nullptr;
116 witness_index = IS_CONSTANT;
117 witness_bool = other;
118 witness_inverted = false;
119 return *this;
121
125template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const bool_t& other) = default;
126
130template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(bool_t&& other)
131{
132 context = other.context;
133 witness_index = other.witness_index;
134 witness_bool = other.witness_bool;
135 witness_inverted = other.witness_inverted;
136 tag = other.tag;
137 return *this;
138}
143template <typename Builder> bool_t<Builder>& bool_t<Builder>::operator=(const witness_t<Builder>& other)
144{
145 BB_ASSERT((other.witness == bb::fr::one()) || (other.witness == bb::fr::zero()));
146 context = other.context;
147 witness_bool = other.witness == bb::fr::one();
148 witness_index = other.witness_index;
149 witness_inverted = false;
150 // Constrain x := other.witness by the relation x^2 = x
151 context->create_bool_gate(witness_index);
152 set_free_witness_tag();
153 return *this;
154}
155
159template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&(const bool_t& other) const
160{
161 Builder* ctx = validate_context<Builder>(context, other.context);
162 bool_t<Builder> result(ctx);
163 bool left = witness_inverted ^ witness_bool;
164 bool right = other.witness_inverted ^ other.witness_bool;
165 result.witness_bool = left && right;
166
167 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
168 if (!is_constant() && !other.is_constant()) {
170 result.witness_index = ctx->add_variable(value);
171
200 int i_a(static_cast<int>(witness_inverted));
201 int i_b(static_cast<int>(other.witness_inverted));
202
203 fr q_m{ 1 - 2 * i_b - 2 * i_a + 4 * i_a * i_b };
204 fr q_l{ i_b * (1 - 2 * i_a) };
205 fr q_r{ i_a * (1 - 2 * i_b) };
206 fr q_o{ -1 };
207 fr q_c{ i_a * i_b };
208
209 ctx->create_arithmetic_gate(
210 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
211 } else if (!is_constant() && other.is_constant()) {
213 // If rhs is a constant true, the output is determined by the lhs. Otherwise the output is a constant
214 // `false`.
215 result = other.witness_bool ? *this : other;
216
217 } else if (is_constant() && !other.is_constant()) {
218 BB_ASSERT(!witness_inverted);
219 // If lhs is a constant true, the output is determined by the rhs. Otherwise the output is a constant
220 // `false`.
221 result = witness_bool ? other : *this;
222 }
223
224 result.tag = OriginTag(tag, other.tag);
225 return result;
226}
227
231template <typename Builder> bool_t<Builder> bool_t<Builder>::operator|(const bool_t& other) const
232{
233 Builder* ctx = validate_context<Builder>(context, other.context);
234
235 bool_t<Builder> result(ctx);
236
237 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
238
239 result.witness_bool = (witness_bool ^ witness_inverted) | (other.witness_bool ^ other.witness_inverted);
241 if (!is_constant() && !other.is_constant()) {
242 result.witness_index = ctx->add_variable(value);
243 // Let
244 // a := lhs = *this;
245 // b := rhs = other;
246 // The result is given by
247 // a + b - a * b = [-(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
248 // [(1 - 2 * i_a) * (1 - i_b)] * w_a
249 // [(1 - 2 * i_b) * (1 - i_a)] * w_b
250 // [i_a + i_b - i_a * i_b] * 1
251 const int rhs_inverted = static_cast<int>(other.witness_inverted);
252 const int lhs_inverted = static_cast<int>(witness_inverted);
253
254 bb::fr q_m{ -(1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted) };
255 bb::fr q_l{ (1 - 2 * lhs_inverted) * (1 - rhs_inverted) };
256 bb::fr q_r{ (1 - lhs_inverted) * (1 - 2 * rhs_inverted) };
257 bb::fr q_o{ bb::fr::neg_one() };
258 bb::fr q_c{ rhs_inverted + lhs_inverted - rhs_inverted * lhs_inverted };
259
260 // Let r := a | b;
261 // Constrain
262 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
263 ctx->create_arithmetic_gate(
264 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
265 } else if (!is_constant() && other.is_constant()) {
266 BB_ASSERT_EQ(other.witness_inverted, false);
267
268 // If we are computing a | b and b is a constant `true`, the result is a constant `true` that does not
269 // depend on `a`.
270 result = other.witness_bool ? other : *this;
271
272 } else if (is_constant() && !other.is_constant()) {
273 // If we are computing a | b and `a` is a constant `true`, the result is a constant `true` that does not
274 // depend on `b`.
275 BB_ASSERT_EQ(witness_inverted, false);
276 result = witness_bool ? *this : other;
277 }
278 result.tag = OriginTag(tag, other.tag);
279 return result;
280}
281
285template <typename Builder> bool_t<Builder> bool_t<Builder>::operator^(const bool_t& other) const
286{
287 Builder* ctx = validate_context<Builder>(context, other.context);
288 bool_t<Builder> result(ctx);
289
290 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
291
292 result.witness_bool = (witness_bool ^ witness_inverted) ^ (other.witness_bool ^ other.witness_inverted);
294
295 if (!is_constant() && !other.is_constant()) {
296 result.witness_index = ctx->add_variable(value);
297 // Let
298 // a := lhs = *this;
299 // b := rhs = other;
300 // The result is given by
301 // a + b - 2 * a * b = [-2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
302 // [(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a
303 // [(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b
304 // [i_a + i_b - 2 * i_a * i_b] * 1]
305 const int rhs_inverted = static_cast<int>(other.witness_inverted);
306 const int lhs_inverted = static_cast<int>(witness_inverted);
307 // Compute the value that's being used in several selectors
308 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
309
310 bb::fr q_m{ -2 * aux_prod };
311 bb::fr q_l{ aux_prod };
312 bb::fr q_r{ aux_prod };
313 bb::fr q_o{ bb::fr::neg_one() };
314 bb::fr q_c{ lhs_inverted + rhs_inverted - 2 * rhs_inverted * lhs_inverted };
315
316 // Let r := a ^ b;
317 // Constrain
318 // q_m * w_a * w_b + q_l * w_a + q_r * w_b + q_o * r + q_c = 0
319 ctx->create_arithmetic_gate(
320 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
321 } else if (!is_constant() && other.is_constant()) {
322 // witness ^ 1 = !witness
323 BB_ASSERT_EQ(other.witness_inverted, false);
324 result = other.witness_bool ? !*this : *this;
325
326 } else if (is_constant() && !other.is_constant()) {
327 BB_ASSERT_EQ(witness_inverted, false);
328 result = witness_bool ? !other : other;
329 }
330 result.tag = OriginTag(tag, other.tag);
331 return result;
332}
336template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!() const
337{
338 bool_t<Builder> result(*this);
339 if (result.is_constant()) {
340 BB_ASSERT(!witness_inverted);
341 // Negate the value of a constant bool_t element.
342 result.witness_bool = !result.witness_bool;
343 } else {
344 // Negate the `inverted` flag of a witnees bool_t element.
345 result.witness_inverted = !result.witness_inverted;
346 }
347 return result;
348}
349
353template <typename Builder> bool_t<Builder> bool_t<Builder>::operator==(const bool_t& other) const
354{
355 Builder* ctx = validate_context<Builder>(context, other.context);
356 bool_t<Builder> result(ctx);
357 BB_ASSERT(ctx || (is_constant() && other.is_constant()));
358
359 result.witness_bool = (witness_bool ^ witness_inverted) == (other.witness_bool ^ other.witness_inverted);
360 if (!is_constant() && !other.is_constant()) {
361 const bb::fr value = result.witness_bool ? bb::fr::one() : bb::fr::zero();
362
363 result.witness_index = context->add_variable(value);
364
365 // Let
366 // a := lhs = *this;
367 // b := rhs = other;
368 // The result is given by
369 // 1 - a - b + 2 a * b = [2 *(1 - 2*i_a) * (1 - 2*i_b)] * w_a w_b +
370 // [-(1 - 2 * i_a) * (1 - 2 * i_b)] * w_a +
371 // [-(1 - 2 * i_b) * (1 - 2 * i_a)] * w_b +
372 // [1 - i_a - i_b + 2 * i_a * i_b] * 1
373 const int rhs_inverted = static_cast<int>(other.witness_inverted);
374 const int lhs_inverted = static_cast<int>(witness_inverted);
375 // Compute the value that's being used in several selectors
376 const int aux_prod = (1 - 2 * rhs_inverted) * (1 - 2 * lhs_inverted);
377 bb::fr q_m{ 2 * aux_prod };
378 bb::fr q_l{ -aux_prod };
379 bb::fr q_r{ -aux_prod };
380 bb::fr q_o{ bb::fr::neg_one() };
381 bb::fr q_c{ 1 - lhs_inverted - rhs_inverted + 2 * rhs_inverted * lhs_inverted };
382
383 ctx->create_arithmetic_gate(
384 { witness_index, other.witness_index, result.witness_index, q_m, q_l, q_r, q_o, q_c });
385
386 } else if (!is_constant() && (other.is_constant())) {
387 // Compare *this with a constant other. If other == true, then we're checking *this == true. In this case we
388 // propagate *this without adding extra constraints, otherwise (if other = false), we propagate !*this.
389 BB_ASSERT_EQ(other.witness_inverted, false);
390 result = other.witness_bool ? *this : !(*this);
391 } else if (is_constant() && !other.is_constant()) {
392 // Completely analogous to the previous case.
393 BB_ASSERT_EQ(witness_inverted, false);
394 result = witness_bool ? other : !other;
395 }
396
397 result.tag = OriginTag(tag, other.tag);
398 return result;
399}
403template <typename Builder> bool_t<Builder> bool_t<Builder>::operator!=(const bool_t<Builder>& other) const
404{
405 return operator^(other);
406}
407
408template <typename Builder> bool_t<Builder> bool_t<Builder>::operator&&(const bool_t<Builder>& other) const
409{
410 return operator&(other);
411}
412
413template <typename Builder> bool_t<Builder> bool_t<Builder>::operator||(const bool_t<Builder>& other) const
414{
415 return operator|(other);
416}
417
421template <typename Builder> void bool_t<Builder>::assert_equal(const bool_t& rhs, std::string const& msg) const
422{
423 const bool_t lhs = *this;
424 Builder* ctx = validate_context<Builder>(rhs.get_context(), lhs.get_context());
425
426 if (lhs.is_constant() && rhs.is_constant()) {
427 BB_ASSERT_EQ(lhs.get_value(), rhs.get_value());
428 } else if (lhs.is_constant()) {
430 // if rhs is inverted, flip the value of the lhs constant
431 const bool lhs_value = rhs.witness_inverted ? !lhs.witness_bool : lhs.witness_bool;
432 ctx->assert_equal_constant(rhs.witness_index, lhs_value, msg);
433 } else if (rhs.is_constant()) {
435 // if lhs is inverted, flip the value of the rhs constant
436 const bool rhs_value = lhs.witness_inverted ? !rhs.witness_bool : rhs.witness_bool;
437 ctx->assert_equal_constant(lhs.witness_index, rhs_value, msg);
438 } else {
439 // Both are witnesses - save original tags and clear them to allow different transcript/free witness sources
440 // (e.g., proving 2 separate properties about same object through 2 different transcripts)
441 const auto lhs_original_tag = lhs.get_origin_tag();
442 const auto rhs_original_tag = rhs.get_origin_tag();
445
446 bool_t left = lhs;
447 bool_t right = rhs;
448 // we need to normalize iff lhs or rhs has an inverted witness (but not both)
449 if (lhs.witness_inverted ^ rhs.witness_inverted) {
450 left = left.normalize();
451 right = right.normalize();
452 }
453 ctx->assert_equal(left.witness_index, right.witness_index, msg);
454
455 // Restore tags
456 lhs.set_origin_tag(lhs_original_tag);
457 rhs.set_origin_tag(rhs_original_tag);
458 }
459}
460
464template <typename Builder>
466 const bool_t& lhs,
467 const bool_t& rhs)
468{
469 if (predicate.is_constant()) {
470 auto result = bool_t(predicate.get_value() ? lhs : rhs);
471 result.set_origin_tag(OriginTag(predicate.get_origin_tag(), lhs.get_origin_tag(), rhs.get_origin_tag()));
472 return result.normalize();
473 }
474
475 bool same = lhs.witness_index == rhs.witness_index;
476 bool witness_same = same && !lhs.is_constant() && (lhs.witness_inverted == rhs.witness_inverted);
477 bool const_same = same && lhs.is_constant() && (lhs.witness_bool == rhs.witness_bool);
478 if (witness_same || const_same) {
479 return lhs.normalize();
480 }
481 // Boolean operations can preserve inverted flags when constants are involved
482 // (e.g., inverted_witness && constant_true returns inverted_witness)
483 return ((predicate && lhs) || (!predicate && rhs)).normalize();
484}
485
489template <typename Builder> bool_t<Builder> bool_t<Builder>::implies(const bool_t<Builder>& other) const
490{
491 // Thanks to negation operator being free, this computation requires at most 1 gate.
492 return (!(*this) || other); // P => Q is equiv. to !P || Q (not(P) or Q).
493}
494
498template <typename Builder> void bool_t<Builder>::must_imply(const bool_t& other, std::string const& msg) const
499{
500 implies(other).assert_equal(true, msg);
501}
502
506template <typename Builder> bool_t<Builder> bool_t<Builder>::implies_both_ways(const bool_t<Builder>& other) const
507{
508 return !((*this) ^ other); // P <=> Q is equiv. to !(P ^ Q) (not(P xor Q)).
509}
510
516template <typename Builder> bool_t<Builder> bool_t<Builder>::normalize() const
517{
518 if (is_constant()) {
519 BB_ASSERT(!witness_inverted);
520 return *this;
521 }
522
523 if (!witness_inverted) {
524 return *this;
525 }
526 // Let a := *this, need to constrain a = a_norm
527 // [1 - 2 i_a] w_a + [-1] w_a_norm + [i_a] = 0
528 // ^ ^ ^
529 // q_l q_o q_c
530 const bool value = witness_bool ^ witness_inverted;
531
532 uint32_t new_witness = context->add_variable(bb::fr{ static_cast<int>(value) });
533
534 const int inverted = static_cast<int>(witness_inverted);
535 bb::fr q_l{ 1 - 2 * inverted };
536 bb::fr q_c{ inverted };
537 bb::fr q_o = bb::fr::neg_one();
538 bb::fr q_m = bb::fr::zero();
539 bb::fr q_r = bb::fr::zero();
540 context->create_arithmetic_gate({ witness_index, context->zero_idx(), new_witness, q_m, q_l, q_r, q_o, q_c });
541
542 witness_index = new_witness;
543 witness_bool = value;
544 witness_inverted = false;
545 return *this;
546}
547
549template class bool_t<bb::MegaCircuitBuilder>;
550
551} // namespace bb::stdlib
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
Implements boolean logic in-circuit.
Definition bool.hpp:59
bool get_value() const
Definition bool.hpp:124
bool is_constant() const
Definition bool.hpp:126
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:153
bool_t implies(const bool_t &other) const
Implements implication operator in circuit.
Definition bool.cpp:489
bool_t normalize() const
A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized...
Definition bool.cpp:516
bool_t operator&(const bool_t &other) const
Implements AND in circuit.
Definition bool.cpp:159
void set_free_witness_tag()
Definition bool.hpp:155
bool_t operator!() const
Implements negation in circuit.
Definition bool.cpp:336
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Conditionally assign lhs or rhs based on predicate, always returns normalized result.
Definition bool.cpp:465
bool_t operator!=(const bool_t &other) const
Implements the not equal operator in circuit.
Definition bool.cpp:403
Builder * get_context() const
Definition bool.hpp:151
Builder * context
Definition bool.hpp:167
uint32_t witness_index
Index of the witness in the builder's witness vector.
Definition bool.hpp:177
bool_t operator&&(const bool_t &other) const
Definition bool.cpp:408
bool_t(const bool value=false)
Construct a constant bool_t object from a bool value.
Definition bool.cpp:21
bool_t operator||(const bool_t &other) const
Definition bool.cpp:413
void must_imply(const bool_t &other, std::string const &msg="bool_t::must_imply") const
Constrains the (a => b) == true.
Definition bool.cpp:498
bool_t & operator=(const bool other)
Assigns a native bool to bool_t object.
Definition bool.cpp:113
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
Definition bool.cpp:421
bool witness_inverted
Definition bool.hpp:169
bool_t operator|(const bool_t &other) const
Implements OR in circuit.
Definition bool.cpp:231
OriginTag tag
Definition bool.hpp:178
static bool_t from_witness_index_unsafe(Builder *ctx, uint32_t witness_index)
Create a bool_t from a witness index that is known to contain a constrained bool value.
Definition bool.cpp:97
bool_t implies_both_ways(const bool_t &other) const
Implements a "double-implication" (<=>), a.k.a "iff", a.k.a. "biconditional".
Definition bool.cpp:506
OriginTag get_origin_tag() const
Definition bool.hpp:154
bool_t operator^(const bool_t &other) const
Implements XOR in circuit.
Definition bool.cpp:285
bool_t operator==(const bool_t &other) const
Implements equality operator in circuit.
Definition bool.cpp:353
StrictMock< MockContext > context
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
static constexpr field neg_one()
static constexpr field one()
static constexpr field zero()