Skip to Content
⚠️ This documentation is AI-generated, for personal use only, and is not supported or endorsed by Google.

Backends

Logic<Field, Backend> delegates every gate operation to a backend: a small type that decides what a “wire” is and what a gate does with one. The same circuit source can compile to a QuadCircuit or directly evaluate on field elements simply by picking a different backend.

A backend must expose:

  • a wire type V;
  • konst(Elt) → V;
  • add, sub, mul (three overloads: V × V, Elt × V, Elt × V × V);
  • the fused forms ax(a, x) = a·x, axy(a, x, y) = a·x·y, axpy(y, a, x) = y + a·x, apy(y, a) = y + a;
  • assert0(V) → V;
  • optionally input_wire() / output_wire(...) for I/O-aware usage.

CompilerBackend

Header: circuits/logic/compiler_backend.h

template <class Field> class CompilerBackend { public: using V = size_t; // wire id explicit CompilerBackend(QuadCircuit<Field>* q); V assert0(const V& a) const; V add(const V& a, const V& b) const; V sub(const V& a, const V& b) const; // add(a, mul(-1, b)) V mul(const V& a, const V& b) const; V mul(const Elt& a, const V& b) const; V mul(const Elt& a, const V& b, const V& c) const; V konst(const Elt& a) const; V ax (const Elt& a, const V& x) const; V axy (const Elt& a, const V& x, const V& y) const; V axpy(const V& y, const Elt& a, const V& x) const; V apy (const V& y, const Elt& a) const; V input_wire() const; void output_wire(size_t n, V wire_id) const; };

Wires are opaque size_t IDs into the underlying QuadCircuit<Field>. Every gate call appends a node; the accumulated circuit can then be emitted, committed, and proved.

Use this backend to compile a circuit once and reuse it across many proofs.

EvaluationBackend

Header: circuits/logic/evaluation_backend.h

template <class Field> class EvaluationBackend { public: struct V { Elt e; V(); explicit V(const Elt& x); Elt elt() const; bool operator==(const V& y) const; bool operator!=(const V& y) const; }; explicit EvaluationBackend(const Field& F, bool panic_on_assertion_failure = true); bool assertion_failed() const; // reads + resets V assert0(const V& a) const; V add(const V& a, const V& b) const; V sub(const V& a, const V& b) const; V mul(const V& a, const V& b) const; V mul(const Elt& a, const V& b) const; V mul(const Elt& a, const V& b, const V& c) const; V konst(const Elt& a) const; V ax (const Elt& a, const V& x) const; V axy (const Elt& a, const V& x, const V& y) const; V axpy(const V& y, const Elt& a, const V& x) const; V apy (const V& y, const Elt& a) const; };

Wires hold actual field elements; every gate is computed eagerly. This backend is primarily used for unit tests and for sanity-checking witnesses before proving.

Assertion modes

By default panic_on_assertion_failure = true and any assert0(non_zero) call aborts via check(false, …) from util/panic.h. Passing false switches to a latching mode: failures set an internal flag that assertion_failed() reads and clears.

The destructor ~EvaluationBackend panics if assertion_failed_ is still true. Tests that opt into the non-panicking mode must read assertion_failed() before the backend goes out of scope, otherwise the process crashes.

Missing input_wire/output_wire

EvaluationBackend does not expose I/O primitives. Because C++ templates are expanded lazily, Logic<..., EvaluationBackend>::input(), output(), vinput(), and voutput() stay uninstantiated — but calling any of them is a compile error. Feed evaluation-mode wires in directly via Logic::konst and collect results with BitW::x.elt().

Last updated on