Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | // Copyright 2019-2022 Cambridge Quantum Computing | |||
2 | // | |||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | |||
4 | // you may not use this file except in compliance with the License. | |||
5 | // You may obtain a copy of the License at | |||
6 | // | |||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | |||
8 | // | |||
9 | // Unless required by applicable law or agreed to in writing, software | |||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | |||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
12 | // See the License for the specific language governing permissions and | |||
13 | // limitations under the License. | |||
14 | ||||
15 | #pragma once | |||
16 | ||||
17 | /** | |||
18 | * @file | |||
19 | * @brief Classical operations | |||
20 | */ | |||
21 | ||||
22 | #include "Op.hpp" | |||
23 | #include "Utils/Json.hpp" | |||
24 | ||||
25 | namespace tket { | |||
26 | ||||
27 | /** | |||
28 | * A purely classical operation. | |||
29 | */ | |||
30 | class ClassicalOp : public Op { | |||
31 | public: | |||
32 | /** | |||
33 | * Construct a ClassicalOp of specified shape | |||
34 | * | |||
35 | * this is a classical operation, only acting on the classical parts of the | |||
36 | * circuit | |||
37 | * | |||
38 | * @param type operation type | |||
39 | * @param n_i number of input-only bits | |||
40 | * @param n_io number of input/output bits | |||
41 | * @param n_o number of output-only bits | |||
42 | * @param name name of operation | |||
43 | */ | |||
44 | ClassicalOp( | |||
45 | OpType type, unsigned n_i, unsigned n_io, unsigned n_o, | |||
46 | const std::string &name = ""); | |||
47 | ||||
48 | // Trivial overrides | |||
49 | ✗ | Op_ptr symbol_substitution( | ||
50 | const SymEngine::map_basic_basic &) const override { | |||
51 | ✗ | return Op_ptr(); | ||
52 | } | |||
53 | ✗ | SymSet free_symbols() const override { return {}; } | ||
54 | ✗ | unsigned n_qubits() const override { return 0; } | ||
55 | ||||
56 | ✗ | op_signature_t get_signature() const override { return sig_; } | ||
57 | ||||
58 | nlohmann::json serialize() const override; | |||
59 | ||||
60 | static Op_ptr deserialize(const nlohmann::json &j); | |||
61 | ||||
62 | std::string get_name(bool latex = false) const override; | |||
63 | ||||
64 | /** Number of input-only bits. */ | |||
65 | 2 | unsigned get_n_i() const { return n_i_; } | ||
66 | ||||
67 | /** Number of input-output bits. */ | |||
68 | 3 | unsigned get_n_io() const { return n_io_; } | ||
69 | ||||
70 | /** Number of output-only bits. */ | |||
71 | 2 | unsigned get_n_o() const { return n_o_; } | ||
72 | ||||
73 | /** | |||
74 | * Equality check between two ClassicalEvalOp instances | |||
75 | */ | |||
76 | bool is_equal(const Op &other) const override; | |||
77 | ||||
78 | protected: | |||
79 | const unsigned n_i_; | |||
80 | const unsigned n_io_; | |||
81 | const unsigned n_o_; | |||
82 | const std::string name_; | |||
83 | std::vector<EdgeType> sig_; | |||
84 | }; | |||
85 | ||||
86 | class ClassicalEvalOp : public ClassicalOp { | |||
87 | public: | |||
88 | /** | |||
89 | * Construct a ClassicalEvalOp of specified shape | |||
90 | * | |||
91 | * this is a classical operation, only acting on the classical parts of the | |||
92 | * Circuit In addition to the ClassicalOp has the class the eval function in | |||
93 | * the signature, which allows a evaluation of this op | |||
94 | * | |||
95 | * @param type operation type | |||
96 | * @param n_i number of input-only bits | |||
97 | * @param n_io number of input/output bits | |||
98 | * @param n_o number of output-only bits | |||
99 | * @param name name of operation | |||
100 | */ | |||
101 | ClassicalEvalOp( | |||
102 | OpType type, unsigned n_i, unsigned n_io, unsigned n_o, | |||
103 | const std::string &name = ""); | |||
104 | ||||
105 | /** | |||
106 | * Evaluation | |||
107 | * | |||
108 | * @param x vector of input bits | |||
109 | * | |||
110 | * @return vector of outbut bits | |||
111 | */ | |||
112 | virtual std::vector<bool> eval(const std::vector<bool> &x) const = 0; | |||
113 | ||||
114 | /** | |||
115 | * Equality check between two ClassicalEvalOp instances | |||
116 | */ | |||
117 | bool is_equal(const Op &other) const override; | |||
118 | }; | |||
119 | ||||
120 | /** | |||
121 | * A general classical operation where all inputs are also outputs | |||
122 | */ | |||
123 | class ClassicalTransformOp : public ClassicalEvalOp { | |||
124 | public: | |||
125 | /** | |||
126 | * Construct from a truth table. | |||
127 | * | |||
128 | * The truth table is represented by a vector of integers such that the j^th | |||
129 | * bit (in little-endian order) of the (sum_i a_i 2^i)^th term is the j^th | |||
130 | * output of the function applied to (a_i). | |||
131 | * | |||
132 | * @param n number of input/output bits | |||
133 | * @param values table of binary-encoded values | |||
134 | * @param name name of operation | |||
135 | * | |||
136 | * @pre n <= 32 | |||
137 | */ | |||
138 | ClassicalTransformOp( | |||
139 | unsigned n, const std::vector<uint32_t> &values, | |||
140 | const std::string &name = "ClassicalTransform"); | |||
141 | ||||
142 | std::vector<bool> eval(const std::vector<bool> &x) const override; | |||
143 | ||||
144 | ✗ | std::vector<uint32_t> get_values() const { return values_; } | ||
145 | ||||
146 | private: | |||
147 | const std::vector<uint32_t> values_; | |||
148 | }; | |||
149 | ||||
150 | /** | |||
151 | * Op containing a classical wasm function call | |||
152 | */ | |||
153 | class WASMOp : public ClassicalOp { | |||
154 | public: | |||
155 | /** | |||
156 | * contains a wasm op that could be added to a circuit. | |||
157 | * This op stores in its signatures which bits are interacting as input and | |||
158 | * output with the call to which function of the wasm file | |||
159 | * | |||
160 | * @param _n total number bits it is interacting with | |||
161 | * @param _ni_vec vector of bits for each input i32 | |||
162 | * @param _no_vec vector of bits for each output i32 | |||
163 | * @param _func_name name of the function | |||
164 | * @param _wasm_uid uid of the wasm file to be called | |||
165 | */ | |||
166 | WASMOp( | |||
167 | unsigned _n, std::vector<unsigned> _ni_vec, std::vector<unsigned> _no_vec, | |||
168 | const std::string &_func_name, const std::string &_wasm_uid); | |||
169 | ||||
170 | /** | |||
171 | * return if the op is external | |||
172 | */ | |||
173 | 1 | bool is_extern() const override { return true; } | ||
174 | ||||
175 | /** | |||
176 | * serialize wasmop to json | |||
177 | */ | |||
178 | nlohmann::json serialize() const override; | |||
179 | ||||
180 | /** | |||
181 | * deserialize json to wasmop | |||
182 | */ | |||
183 | static Op_ptr deserialize(const nlohmann::json &j); | |||
184 | ||||
185 | /** | |||
186 | * Equality check between two WASMOp instances | |||
187 | */ | |||
188 | bool is_equal(const Op &other) const override; | |||
189 | ||||
190 | /** | |||
191 | * returns the number of classical bits the wasm op is acting on | |||
192 | */ | |||
193 | 7 | unsigned get_n() const { return n_; } | ||
194 | ||||
195 | /** | |||
196 | * returns the number of i32 the function is acting on | |||
197 | */ | |||
198 | ✗ | unsigned get_n_i32() const { return n_i32_; } | ||
199 | ||||
200 | /** | |||
201 | * returns the vector of number of bit used for each of the input i32 | |||
202 | * variables | |||
203 | */ | |||
204 | 5 | std::vector<unsigned> get_ni_vec() const { return ni_vec_; } | ||
205 | ||||
206 | /** | |||
207 | * returns the vector of number of bit used for each of the output i32 | |||
208 | * variables | |||
209 | */ | |||
210 | 5 | std::vector<unsigned> get_no_vec() const { return no_vec_; } | ||
211 | ||||
212 | /** | |||
213 | * returns the name of the function the wasm op is using | |||
214 | */ | |||
215 | ✗ | std::string get_func_name() const { return func_name_; } | ||
216 | ||||
217 | /** | |||
218 | * returns the uid of the wasm file the op is using, the file is stored on the | |||
219 | * python layer | |||
220 | */ | |||
221 | ✗ | std::string get_wasm_uid() const { return wasm_uid_; } | ||
222 | ||||
223 | private: | |||
224 | /** | |||
225 | * total number of classical bits the op is interacting with | |||
226 | */ | |||
227 | const unsigned n_; | |||
228 | ||||
229 | /** | |||
230 | * total number of i32 inut and output variables | |||
231 | */ | |||
232 | const unsigned n_i32_; | |||
233 | ||||
234 | /** | |||
235 | * vector of bits for each input i32 | |||
236 | */ | |||
237 | const std::vector<unsigned> ni_vec_; | |||
238 | ||||
239 | /** | |||
240 | * vector of bits for each output i32 | |||
241 | */ | |||
242 | const std::vector<unsigned> no_vec_; | |||
243 | ||||
244 | /** | |||
245 | * name of the called function | |||
246 | */ | |||
247 | const std::string func_name_; | |||
248 | ||||
249 | /** | |||
250 | * uid of the wasm file the op is using | |||
251 | */ | |||
252 | const std::string wasm_uid_; | |||
253 | }; | |||
254 | ||||
255 | /** | |||
256 | * An operation to set some bits to specified values | |||
257 | */ | |||
258 | class SetBitsOp : public ClassicalEvalOp { | |||
259 | public: | |||
260 | /** | |||
261 | * Construct from values. | |||
262 | * | |||
263 | * @param values values to set | |||
264 | */ | |||
265 | 3 | explicit SetBitsOp(const std::vector<bool> &values) | ||
266 | 3 | : ClassicalEvalOp(OpType::SetBits, 0, 0, values.size(), "SetBits"), | ||
267 |
3/6✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
|
6 | values_(values) {} | |
268 | ||||
269 | std::string get_name(bool latex) const override; | |||
270 | ||||
271 | ✗ | std::vector<bool> get_values() const { return values_; } | ||
272 | ||||
273 | std::vector<bool> eval(const std::vector<bool> &x) const override; | |||
274 | ||||
275 | private: | |||
276 | std::vector<bool> values_; | |||
277 | }; | |||
278 | ||||
279 | /** | |||
280 | * An operation to copy some bit values | |||
281 | * | |||
282 | * @param n number of bits copied | |||
283 | */ | |||
284 | class CopyBitsOp : public ClassicalEvalOp { | |||
285 | public: | |||
286 | ✗ | explicit CopyBitsOp(unsigned n) | ||
287 | ✗ | : ClassicalEvalOp(OpType::CopyBits, n, 0, n, "CopyBits") {} | ||
288 | ||||
289 | std::vector<bool> eval(const std::vector<bool> &x) const override; | |||
290 | }; | |||
291 | ||||
292 | /** | |||
293 | * A classical operation with single output bit. | |||
294 | * | |||
295 | * There may be any number of input bits. The output bit is distinct from these. | |||
296 | */ | |||
297 | class PredicateOp : public ClassicalEvalOp { | |||
298 | public: | |||
299 | /** | |||
300 | * Construct a PredicateOp of specified arity | |||
301 | * | |||
302 | * @param type type of classical operation | |||
303 | * @param n number of input bits | |||
304 | * @param name name of operation | |||
305 | */ | |||
306 | ✗ | PredicateOp(OpType type, unsigned n, const std::string &name = "") | ||
307 | ✗ | : ClassicalEvalOp(type, n, 0, 1, name) {} | ||
308 | }; | |||
309 | ||||
310 | /** | |||
311 | * A predicate defined by a range of values in binary encoding | |||
312 | */ | |||
313 | class RangePredicateOp : public PredicateOp { | |||
314 | public: | |||
315 | /** | |||
316 | * Construct from a lower and upper bound | |||
317 | * | |||
318 | * The lower and upper bounds are both inclusive. The output is set to 1 if | |||
319 | * and only the encoded number is in the specified range. | |||
320 | * | |||
321 | * @param n number of inputs to predicate | |||
322 | * @param a lower bound in little-endian encoding | |||
323 | * @param b upper bound in little-endian encoding | |||
324 | */ | |||
325 | ✗ | RangePredicateOp( | ||
326 | unsigned n, uint32_t a = 0, | |||
327 | uint32_t b = std::numeric_limits<uint32_t>::max()) | |||
328 | ✗ | : PredicateOp(OpType::RangePredicate, n, "RangePredicate"), a(a), b(b) {} | ||
329 | ||||
330 | std::string get_name(bool latex) const override; | |||
331 | ||||
332 | ✗ | uint32_t upper() const { return b; } | ||
333 | ||||
334 | ✗ | uint32_t lower() const { return a; } | ||
335 | ||||
336 | std::vector<bool> eval(const std::vector<bool> &x) const override; | |||
337 | ||||
338 | /** | |||
339 | * Equality check between two RangePredicateOp instances | |||
340 | */ | |||
341 | bool is_equal(const Op &other) const override; | |||
342 | ||||
343 | private: | |||
344 | uint32_t a; | |||
345 | uint32_t b; | |||
346 | }; | |||
347 | ||||
348 | /** | |||
349 | * A predicate defined explicitly by a truth table | |||
350 | */ | |||
351 | class ExplicitPredicateOp : public PredicateOp { | |||
352 | public: | |||
353 | /** | |||
354 | * @brief Construct from a table of values | |||
355 | * | |||
356 | * The truth table is represented by a vector of bool whose | |||
357 | * (sum_i a_i 2^i)^th term is the predicate applied to (a_i). | |||
358 | * | |||
359 | * @param n number of inputs to predicate | |||
360 | * @param values table of values | |||
361 | * @param name name of operation | |||
362 | * | |||
363 | * @pre n <= 32 | |||
364 | */ | |||
365 | ExplicitPredicateOp( | |||
366 | unsigned n, const std::vector<bool> &values, | |||
367 | const std::string &name = "ExplicitPredicate"); | |||
368 | ||||
369 | std::vector<bool> eval(const std::vector<bool> &x) const override; | |||
370 | ||||
371 | ✗ | std::vector<bool> get_values() const { return values_; } | ||
372 | ||||
373 | private: | |||
374 | const std::vector<bool> values_; | |||
375 | }; | |||
376 | ||||
377 | /** | |||
378 | * A classical operation with one output bit which is also an input bit | |||
379 | */ | |||
380 | class ModifyingOp : public ClassicalEvalOp { | |||
381 | public: | |||
382 | /** | |||
383 | * Construct a ModifyingOp of specified arity | |||
384 | * | |||
385 | * @param type type of classical operation | |||
386 | * @param n number of input bits in addition to the modified bit | |||
387 | * @param name name of operation | |||
388 | */ | |||
389 | 2 | ModifyingOp(OpType type, unsigned n, const std::string &name) | ||
390 | 2 | : ClassicalEvalOp(type, n, 1, 0, name) {} | ||
391 | }; | |||
392 | ||||
393 | /** | |||
394 | * A modifying operation defined explicitly by a truth table | |||
395 | */ | |||
396 | class ExplicitModifierOp : public ModifyingOp { | |||
397 | public: | |||
398 | /** | |||
399 | * @brief Construct from a table of values | |||
400 | * | |||
401 | * The truth table is represented by a vector of bool whose | |||
402 | * (sum_i a_i 2^i)^th term is the predicate applied to (a_i), where a_m is | |||
403 | * the initial value of the modified bit. | |||
404 | * | |||
405 | * @param n number of inputs to predicate in addition to the modified bit | |||
406 | * @param values table of values | |||
407 | * @param name name of operation | |||
408 | * | |||
409 | * @pre n <= 31 | |||
410 | */ | |||
411 | ExplicitModifierOp( | |||
412 | unsigned n, const std::vector<bool> &values, | |||
413 | const std::string &name = "ExplicitModifier"); | |||
414 | ||||
415 | std::vector<bool> eval(const std::vector<bool> &x) const override; | |||
416 | ||||
417 | ✗ | std::vector<bool> get_values() const { return values_; } | ||
418 | ||||
419 | private: | |||
420 | const std::vector<bool> values_; | |||
421 | }; | |||
422 | ||||
423 | /** | |||
424 | * A classical operation applied simultaneously to multiple bits. | |||
425 | * | |||
426 | * The order of arguments is: all arguments to first operation, then all | |||
427 | * arguments to second operation, and so on. | |||
428 | */ | |||
429 | class MultiBitOp : public ClassicalEvalOp { | |||
430 | public: | |||
431 | MultiBitOp(std::shared_ptr<const ClassicalEvalOp> op, unsigned n); | |||
432 | ||||
433 | std::string get_name(bool latex) const override; | |||
434 | ||||
435 | ✗ | std::shared_ptr<const ClassicalEvalOp> get_op() const { return op_; } | ||
436 | ||||
437 | ✗ | unsigned get_n() const { return n_; } | ||
438 | ||||
439 | std::vector<bool> eval(const std::vector<bool> &x) const override; | |||
440 | ||||
441 | /** | |||
442 | * Equality check between two MultiBitOp instances | |||
443 | */ | |||
444 | bool is_equal(const Op &other) const override; | |||
445 | ||||
446 | private: | |||
447 | std::shared_ptr<const ClassicalEvalOp> op_; | |||
448 | unsigned n_; | |||
449 | }; | |||
450 | ||||
451 | /** | |||
452 | * Classical NOT transform | |||
453 | */ | |||
454 | std::shared_ptr<ClassicalTransformOp> ClassicalX(); | |||
455 | ||||
456 | /** | |||
457 | * Classical CNOT transform | |||
458 | */ | |||
459 | std::shared_ptr<ClassicalTransformOp> ClassicalCX(); | |||
460 | ||||
461 | /** | |||
462 | * Unary NOT operator | |||
463 | */ | |||
464 | std::shared_ptr<ExplicitPredicateOp> NotOp(); | |||
465 | ||||
466 | /** | |||
467 | * Binary AND operator | |||
468 | */ | |||
469 | std::shared_ptr<ExplicitPredicateOp> AndOp(); | |||
470 | ||||
471 | /** | |||
472 | * Binary OR operator | |||
473 | */ | |||
474 | std::shared_ptr<ExplicitPredicateOp> OrOp(); | |||
475 | ||||
476 | /** | |||
477 | * Binary XOR operator | |||
478 | */ | |||
479 | std::shared_ptr<ExplicitPredicateOp> XorOp(); | |||
480 | ||||
481 | /** | |||
482 | * In-place AND with another input | |||
483 | */ | |||
484 | std::shared_ptr<ExplicitModifierOp> AndWithOp(); | |||
485 | ||||
486 | /** | |||
487 | * In-place OR with another input | |||
488 | */ | |||
489 | std::shared_ptr<ExplicitModifierOp> OrWithOp(); | |||
490 | ||||
491 | /** | |||
492 | * In-place XOR with another input | |||
493 | */ | |||
494 | std::shared_ptr<ExplicitModifierOp> XorWithOp(); | |||
495 | ||||
496 | } // namespace tket | |||
497 |