barretenberg
Loading...
Searching...
No Matches
circuit.hpp
1#pragma once
2#include <fstream>
3#include <limits>
4#include <sstream>
5#include <string>
6#include <unordered_map>
7
8#include "barretenberg/serialize/cbind.hpp"
9#include "barretenberg/serialize/msgpack.hpp"
10
11#include "barretenberg/smt_verification/terms/bool.hpp"
12#include "barretenberg/smt_verification/terms/ffiterm.hpp"
13#include "barretenberg/smt_verification/terms/ffterm.hpp"
14
15namespace smt_circuit {
16using namespace smt_solver;
17using namespace smt_terms;
18
20 std::string modulus;
21 std::vector<uint32_t> public_inps;
22 std::unordered_map<uint32_t, std::string> vars_of_interest;
23 std::vector<barretenberg::fr> variables;
24 std::vector<std::vector<barretenberg::fr>> selectors;
25 std::vector<std::vector<uint32_t>> wires;
26 MSGPACK_FIELDS(modulus, public_inps, vars_of_interest, variables, selectors, wires);
27};
28
37template <typename FF> class Circuit {
38 private:
39 void init();
40 void add_gates();
41
42 public:
43 std::vector<std::string> variables; // circuit witness
44 std::vector<uint32_t> public_inps; // public inputs from the circuit
45 std::unordered_map<uint32_t, std::string> vars_of_interest; // names of the variables
46 std::unordered_map<std::string, uint32_t> terms; // inverse map of the previous memeber
47 std::vector<std::vector<std::string>> selectors; // selectors from the circuit
48 std::vector<std::vector<uint32_t>> wires_idxs; // values of the gates' wires
49 std::vector<FF> vars; // all the symbolic variables from the circuit
50
51 Solver* solver; // pointer to the solver
52 std::string tag; // tag of the symbolic circuit.
53 // If not empty, will be added to the names
54 // of symbolic variables to prevent collisions.
55
56 explicit Circuit(CircuitSchema& circuit_info, Solver* solver, const std::string& tag = "");
57
58 FF operator[](const std::string& name);
59 FF operator[](const uint32_t& idx) { return vars[idx]; };
60 inline uint32_t get_num_gates() const { return static_cast<uint32_t>(selectors.size()); };
61 inline uint32_t get_num_vars() const { return static_cast<uint32_t>(vars.size()); };
62};
63
71template <typename FF>
72Circuit<FF>::Circuit(CircuitSchema& circuit_info, Solver* solver, const std::string& tag)
73 : public_inps(circuit_info.public_inps)
74 , vars_of_interest(circuit_info.vars_of_interest)
75 , wires_idxs(circuit_info.wires)
76 , solver(solver)
77 , tag(tag)
78{
79 if (!this->tag.empty()) {
80 if (this->tag[0] != '_') {
81 this->tag = "_" + this->tag;
82 }
83 }
84
85 for (auto var : circuit_info.variables) {
86 std::stringstream buf; // TODO(alex): looks bad. Would be great to create tostring() converter
87 buf << var;
88 std::string tmp = buf.str();
89 tmp[1] = '0'; // avoiding `x` in 0x prefix
90 variables.push_back(tmp);
91 }
92
93 for (auto& x : vars_of_interest) {
94 terms.insert({ x.second, x.first });
95 }
96
97 vars_of_interest.insert({ 0, "zero" });
98 vars_of_interest.insert({ 1, "one" });
99 terms.insert({ "zero", 0 });
100 terms.insert({ "one", 1 });
101
102 for (auto sel : circuit_info.selectors) {
103 std::vector<std::string> tmp_sel;
104 for (size_t j = 0; j < 5; j++) {
105 std::stringstream buf; // TODO(alex): #2
106 buf << sel[j];
107 std::string q_i = buf.str();
108 q_i[1] = '0'; // avoiding `x` in 0x prefix
109 tmp_sel.push_back(q_i);
110 }
111 selectors.push_back(tmp_sel);
112 }
113
114 this->init();
115 this->add_gates();
116}
117
123template <typename FF> void Circuit<FF>::init()
124{
125 size_t num_vars = variables.size();
126
127 vars.push_back(FF::Var("zero" + this->tag, this->solver));
128 vars.push_back(FF::Var("one" + this->tag, this->solver));
129
130 for (size_t i = 2; i < num_vars; i++) {
131 if (vars_of_interest.contains(static_cast<uint32_t>(i))) {
132 std::string name = vars_of_interest[static_cast<uint32_t>(i)];
133 vars.push_back(FF::Var(name + this->tag, this->solver));
134 } else {
135 vars.push_back(FF::Var("var_" + std::to_string(i) + this->tag, this->solver));
136 }
137 }
138
139 vars[0] == FF::Const("0", this->solver);
140 vars[1] == FF::Const("1", this->solver);
141
142 for (auto i : public_inps) {
143 vars[i] == FF::Const(variables[i], this->solver);
144 }
145}
146
151template <typename FF> void Circuit<FF>::add_gates()
152{
153 for (size_t i = 0; i < get_num_gates(); i++) {
154 FF q_m = FF::Const(selectors[i][0], this->solver);
155 FF q_1 = FF::Const(selectors[i][1], this->solver);
156 FF q_2 = FF::Const(selectors[i][2], this->solver);
157 FF q_3 = FF::Const(selectors[i][3], this->solver);
158 FF q_c = FF::Const(selectors[i][4], this->solver);
159
160 uint32_t w_l = wires_idxs[i][0];
161 uint32_t w_r = wires_idxs[i][1];
162 uint32_t w_o = wires_idxs[i][2];
163
164 // Binary gate. Relaxes the solver.
165 // TODO(alex): Probably we can add other basic gates here too to relax the stuff.
166 // TODO(alex): Theoretically this can be applyed after we ensure that the block of polynomial equations holds
167 // and then simplify that block in future to relax the solver constraint system. Seems like a hard one to
168 // implement or actually to automate, but I'll think on it for a while. it will probably require to split
169 // add_gates and init methods into more complex/generalized parts.
170 if (w_l == w_r && w_r == w_o) {
171 if (std::string(q_m) == "1" && std::string(q_1) == "0" && std::string(q_2) == "0" &&
172 std::string(q_3) == "-1" && std::string(q_c) == "0") { // squaring gate
173 (Bool(vars[w_l]) == Bool(vars[0]) | Bool(vars[w_l]) == Bool(vars[1])).assert_term();
174 }
175 }
176
177 FF eq = vars[0];
178
179 // mult selector
180 if (std::string(q_m) != "0") {
181 eq += q_m * vars[w_l] * vars[w_r];
182 }
183 // w_l selector
184 if (std::string(q_1) != "0") {
185 eq += q_1 * vars[w_l];
186 }
187 // w_r selector
188 if (std::string(q_2) != "0") {
189 eq += q_2 * vars[w_r];
190 }
191 // w_o selector
192 if (std::string(q_3) != "0") {
193 eq += q_3 * vars[w_o];
194 }
195 // w_c selector
196 if (std::string(q_c) != "0") {
197 eq += q_c;
198 }
199 eq == vars[0];
200 }
201}
202
209template <typename FF> FF Circuit<FF>::operator[](const std::string& name)
210{
211 if (!this->terms.contains(name)) {
212 throw std::length_error("No such an item " + name + " in vars or it vas not declared as interesting");
213 }
214 uint32_t idx = this->terms[name];
215 return this->vars[idx];
216}
217
218CircuitSchema unpack_from_buffer(const msgpack::sbuffer& buf);
219CircuitSchema unpack_from_file(const std::string& fname);
220
221template <typename FF>
222std::pair<Circuit<FF>, Circuit<FF>> unique_witness(CircuitSchema& circuit_info,
223 Solver* s,
224 const std::vector<std::string>& equal = {},
225 const std::vector<std::string>& not_equal = {},
226 const std::vector<std::string>& equal_at_the_same_time = {},
227 const std::vector<std::string>& not_eqaul_at_the_same_time = {});
228
229extern template std::pair<Circuit<FFTerm>, Circuit<FFTerm>> unique_witness(
230 CircuitSchema& circuit_info,
231 Solver* s,
232 const std::vector<std::string>& equal = {},
233 const std::vector<std::string>& not_equal = {},
234 const std::vector<std::string>& equal_at_the_same_time = {},
235 const std::vector<std::string>& not_eqaul_at_the_same_time = {});
236
237extern template std::pair<Circuit<FFITerm>, Circuit<FFITerm>> unique_witness(
238 CircuitSchema& circuit_info,
239 Solver* s,
240 const std::vector<std::string>& equal = {},
241 const std::vector<std::string>& not_equal = {},
242 const std::vector<std::string>& equal_at_the_same_time = {},
243 const std::vector<std::string>& not_eqaul_at_the_same_time = {});
244
245}; // namespace smt_circuit
Symbolic Circuit class.
Definition: circuit.hpp:37
FF operator[](const std::string &name)
Returns a previously named symbolic variable.
Definition: circuit.hpp:209
Class for the solver.
Definition: solver.hpp:28
Bool element class.
Definition: bool.hpp:15
Definition: acir.hpp:902
Definition: circuit.hpp:19