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().