32static const unsigned MAX_N_CONTROLS = 32;
42static Circuit multiplexor_sequential_decomp(
43 const ctrl_op_map_t &op_map,
unsigned n_controls,
unsigned n_targets) {
44 Circuit c(n_controls + n_targets);
45 std::vector<unsigned> qubits(n_controls + n_targets);
46 std::iota(std::begin(qubits), std::end(qubits), 0);
47 for (
auto it = op_map.begin(); it != op_map.end(); it++) {
48 std::vector<unsigned> zero_ctrls;
49 for (
unsigned i = 0; i < it->first.size(); i++) {
51 zero_ctrls.push_back(i);
56 c.add_box(qcbox, qubits);
57 for (
unsigned i : zero_ctrls) {
100static void recursive_demultiplex_rotation(
101 const std::vector<Expr> &angles,
const OpType &axis,
unsigned total_qubits,
103 unsigned n_rotations = angles.size();
104 unsigned n_qubits = (unsigned)log2(n_rotations) + 1;
105 unsigned mid = (unsigned)(n_rotations / 2);
106 std::vector<Expr> p_angles;
107 std::vector<Expr> q_angles;
108 for (
unsigned i = 0; i < mid; i++) {
109 p_angles.push_back((angles[i] - angles[mid + i]) / 2);
110 q_angles.push_back((angles[i] + angles[mid + i]) / 2);
116 std::swap(p_angles, q_angles);
118 if (q_angles.size() == 1) {
120 commands.push_back(GateSpec(axis, q_angles[0]));
122 recursive_demultiplex_rotation(
125 commands.push_back(GateSpec(
OpType::CX, total_qubits - n_qubits));
126 if (p_angles.size() == 1) {
128 commands.push_back(GateSpec(axis, p_angles[0]));
130 recursive_demultiplex_rotation(
135 commands.push_back(GateSpec(
OpType::CX, total_qubits - n_qubits));
149static std::tuple<Eigen::Matrix2cd, Eigen::Matrix2cd, double, double>
150constant_demultiplex(
const Eigen::Matrix2cd &a,
const Eigen::Matrix2cd &b) {
151 Eigen::Matrix2cd
X = a * b.adjoint();
155 {tk1_params[0], tk1_params[1], tk1_params[2], 0.0});
157 double phi = tk1_params[3] *
PI * 2;
159 double a0 = -
PI / 2 - phi / 2 - std::arg(x0);
160 double a1 =
PI / 2 - phi / 2 + std::arg(x0);
163 Eigen::Matrix2cd r = Eigen::Matrix2cd::Zero();
170 Eigen::ComplexEigenSolver<Eigen::Matrix2cd> eigen_solver(r *
X * r);
171 Eigen::Matrix2cd u = eigen_solver.eigenvectors();
174 if (std::abs(eigen_solver.eigenvalues()[0] +
i_) <
EPS) {
175 u.col(0).swap(u.col(1));
177 Eigen::Matrix2cd d = Eigen::Matrix2cd::Zero();
178 d(0, 0) = std::sqrt(
i_);
179 d(1, 1) = std::sqrt(-
i_);
180 Eigen::Matrix2cd v = d * u.adjoint() * r.adjoint() * b;
181 return {u, v, a0, a1};
191static std::vector<Eigen::Matrix2cd> ucrz_angles_to_diagonal(
192 const std::vector<double> &angles) {
193 std::vector<Eigen::Matrix2cd> diag;
194 unsigned mid = (unsigned)(angles.size() / 2);
195 for (
unsigned i = 0; i < mid; i++) {
196 Eigen::Matrix2cd u = Eigen::Matrix2cd::Zero();
197 u(0, 0) = std::exp(-0.5 * angles[i] *
i_ *
PI);
198 u(1, 1) = std::exp(-0.5 * angles[i + mid] *
i_ *
PI);
201 for (
unsigned i = 0; i < mid; i++) {
202 Eigen::Matrix2cd u = Eigen::Matrix2cd::Zero();
203 u(0, 0) = std::exp(0.5 * angles[i] *
i_ *
PI);
204 u(1, 1) = std::exp(0.5 * angles[i + mid] *
i_ *
PI);
211 unsigned n_controls = 0;
212 unsigned n_targets = 0;
213 for (
auto it = op_map.begin(); it != op_map.end(); it++) {
215 if ((
unsigned long)std::count(
218 "Multiplexed operations cannot have classical wires.",
219 it->second->get_type());
221 if (it == op_map.begin()) {
222 n_controls = (unsigned)it->first.size();
223 n_targets = (unsigned)op_sig.size();
225 if (it->first.size() != n_controls) {
226 throw std::invalid_argument(
227 "The bitstrings passed to the multiplexor must have the same "
230 if (op_sig.size() != n_targets) {
231 throw std::invalid_argument(
232 "Multiplexed operations must have the same width.");
271static void recursive_demultiplex_u2(
272 std::vector<Eigen::Matrix2cd> &unitaries,
unsigned total_qubits,
273 std::vector<GateSpec> &commands,
float &phase,
274 std::vector<std::vector<double>> &ucrzs,
275 const Eigen::Matrix2cd &left_compose,
276 const Eigen::Matrix2cd &right_compose) {
279 const static Eigen::Matrix2cd U_MULT =
281 const static Eigen::Matrix2cd V_MULT =
283 unsigned n_unitaries = unitaries.size();
284 unsigned n_qubits = (unsigned)log2(n_unitaries) + 1;
285 unsigned mid = (unsigned)(n_unitaries / 2);
292 std::vector<Eigen::Matrix2cd> u_list;
293 std::vector<Eigen::Matrix2cd> v_list;
294 std::vector<double> rz_list(n_unitaries);
297 std::vector<Eigen::Matrix2cd> ucrz_diag =
298 ucrz_angles_to_diagonal(ucrzs[n_qubits - 2]);
299 for (
unsigned i = 0; i < unitaries.size(); i++) {
300 unitaries[i] = unitaries[i] * ucrz_diag[i];
303 for (
unsigned i = 0; i < mid; i++) {
304 auto [u, v, a0, a1] =
305 constant_demultiplex(unitaries[i], unitaries[mid + i]);
309 rz_list[i + mid] = a1;
313 std::for_each(rz_list.begin(), rz_list.end(), [](
double &f) { f += 1.5; });
314 ucrzs[n_qubits - 2] = rz_list;
318 if (v_list.size() == 1) {
319 Eigen::Matrix2cd v_prime = V_MULT * v_list[0] * left_compose;
320 commands.push_back(GateSpec(
OpType::U1, v_prime));
322 recursive_demultiplex_u2(
323 v_list, total_qubits, commands, phase, ucrzs, left_compose, V_MULT);
326 commands.push_back(GateSpec(
OpType::CX, total_qubits - n_qubits));
330 if (u_list.size() == 1) {
331 Eigen::Matrix2cd u_prime = right_compose * u_list[0] * U_MULT;
332 commands.push_back(GateSpec(
OpType::U1, u_prime));
334 recursive_demultiplex_u2(
335 u_list, total_qubits, commands, phase, ucrzs, U_MULT, right_compose);
341 const SymEngine::map_basic_basic &sub_map,
const ctrl_op_map_t &op_map) {
343 for (
auto it = op_map.begin(); it != op_map.end(); it++) {
344 new_op_map.insert({it->first, it->second->symbol_substitution(sub_map)});
351 for (
auto it = op_map.begin(); it != op_map.end(); it++) {
352 SymSet op_symbols = it->second->free_symbols();
353 all_symbols.insert(op_symbols.begin(), op_symbols.end());
360 for (
auto it = op_map.begin(); it != op_map.end(); it++) {
361 new_op_map.insert({it->first, it->second->dagger()});
368 for (
auto it = op_map.begin(); it != op_map.end(); it++) {
369 new_op_map.insert({it->first, it->second->transpose()});
374static bool opmap_it_equal(
375 const std::pair<std::vector<bool>,
Op_ptr> &lhs,
376 const std::pair<std::vector<bool>,
Op_ptr> &rhs) {
377 return lhs.first == rhs.first && *lhs.second == *rhs.second;
379static bool tensored_opmap_it_equal(
380 const std::pair<std::vector<bool>, std::vector<Op_ptr>> &lhs,
381 const std::pair<std::vector<bool>, std::vector<Op_ptr>> &rhs) {
382 return lhs.first == rhs.first &&
384 lhs.second.begin(), lhs.second.end(), rhs.second.begin(),
386 [](
const Op_ptr &a,
const Op_ptr &b) { return *a == *b; });
390static bool opmap_compare(
393 map1.begin(), map1.end(), map2.begin(), map2.end(), opmap_it_equal);
396static bool opmap_compare(
399 map1.begin(), map1.end(), map2.begin(), map2.end(),
400 tensored_opmap_it_equal);
405 auto it = op_map.begin();
406 if (it == op_map.end()) {
407 throw std::invalid_argument(
408 "The op_map argument passed to MultiplexorBox cannot be empty.");
410 n_controls_ = (unsigned)it->first.size();
411 n_targets_ = it->second->n_qubits();
412 op_map_validate(op_map);
417 n_controls_(other.n_controls_),
418 n_targets_(other.n_targets_),
419 op_map_(other.op_map_) {}
422 const SymEngine::map_basic_basic &sub_map)
const {
423 ctrl_op_map_t new_op_map = op_map_symbol_sub(sub_map, op_map_);
424 return std::make_shared<MultiplexorBox>(new_op_map);
428 return op_map_free_symbols(op_map_);
432 return std::make_shared<MultiplexorBox>(op_map_dagger(op_map_));
436 return std::make_shared<MultiplexorBox>(op_map_transpose(op_map_));
447 return opmap_compare(op_map_, other.op_map_);
453 j[
"op_map"] = box.get_op_map();
461 boost::lexical_cast<boost::uuids::uuid>(j.at(
"id").get<std::string>()));
465 circ_ = std::make_shared<Circuit>(
466 multiplexor_sequential_decomp(op_map_, n_controls_, n_targets_));
471 auto it = op_map.begin();
472 if (it == op_map.end()) {
473 throw std::invalid_argument(
474 "The op_map argument passed to MultiplexedRotationBox cannot be "
477 for (; it != op_map.end(); it++) {
478 if (it == op_map.begin()) {
479 n_controls_ = (unsigned)it->first.size();
480 if (n_controls_ > MAX_N_CONTROLS) {
481 throw std::invalid_argument(
482 "MultiplexedRotationBox only supports bitstrings up to " +
483 std::to_string(MAX_N_CONTROLS) +
" bits.");
485 axis_ = it->second->get_type();
488 "Ops passed to MultiplexedRotationBox must be either Rx, Ry, or "
493 if (it->second->get_type() != axis_) {
494 throw std::invalid_argument(
495 "Ops passed to MultiplexedRotationBox must have the same rotation "
500 op_map_validate(op_map);
506 n_controls_(other.n_controls_),
507 op_map_(other.op_map_),
508 axis_(other.axis_) {}
511 const SymEngine::map_basic_basic &sub_map)
const {
512 ctrl_op_map_t new_op_map = op_map_symbol_sub(sub_map, op_map_);
513 return std::make_shared<MultiplexedRotationBox>(new_op_map);
517 return op_map_free_symbols(op_map_);
521 return std::make_shared<MultiplexedRotationBox>(op_map_dagger(op_map_));
525 return std::make_shared<MultiplexedRotationBox>(op_map_transpose(op_map_));
537 return opmap_compare(op_map_, other.op_map_);
543 j[
"op_map"] = box.get_op_map();
552 boost::lexical_cast<boost::uuids::uuid>(j.at(
"id").get<std::string>()));
556 unsigned long long n_rotations = 1ULL << n_controls_;
557 std::vector<Expr> rotations(n_rotations);
559 for (
unsigned long long i = 0; i < n_rotations; i++) {
560 auto it = op_map_.find(
dec_to_bin(i, n_controls_));
561 if (it == op_map_.end()) {
564 rotations[i] = it->second->get_params()[0];
567 std::vector<GateSpec> commands;
573 recursive_demultiplex_rotation(
583 if (n_controls_ == 0) {
584 auto it = op_map_.begin();
585 circ.
add_op<
unsigned>(it->second, {0});
586 circ_ = std::make_shared<Circuit>(circ);
613 circ_ = std::make_shared<Circuit>(circ);
618 auto it = op_map.begin();
619 if (it == op_map.end()) {
620 throw std::invalid_argument(
621 "The op_map argument passed to MultiplexedU2Box cannot be empty.");
623 n_controls_ = (unsigned)it->first.size();
624 if (n_controls_ > MAX_N_CONTROLS) {
625 throw std::invalid_argument(
626 "MultiplexedU2Box only supports bitstrings up to " +
627 std::to_string(MAX_N_CONTROLS) +
" bits.");
629 for (; it != op_map.end(); it++) {
630 OpType optype = it->second->get_type();
634 "Ops passed to MultiplexedU2Box must be single-qubit unitary gate "
635 "types or Unitary1qBox.",
639 op_map_validate(op_map);
644 n_controls_(other.n_controls_),
645 op_map_(other.op_map_),
646 impl_diag_(other.impl_diag_) {}
649 const SymEngine::map_basic_basic &sub_map)
const {
650 ctrl_op_map_t new_op_map = op_map_symbol_sub(sub_map, op_map_);
651 return std::make_shared<MultiplexedU2Box>(new_op_map, impl_diag_);
655 return op_map_free_symbols(op_map_);
659 return std::make_shared<MultiplexedU2Box>(op_map_dagger(op_map_), impl_diag_);
663 return std::make_shared<MultiplexedU2Box>(
664 op_map_transpose(op_map_), impl_diag_);
676 return impl_diag_ == other.impl_diag_ &&
677 opmap_compare(op_map_, other.op_map_);
683 j[
"op_map"] = box.get_op_map();
684 j[
"impl_diag"] = box.get_impl_diag();
690 j.at(
"op_map").get<
ctrl_op_map_t>(), j.at(
"impl_diag").get<
bool>());
693 boost::lexical_cast<boost::uuids::uuid>(j.at(
"id").get<std::string>()));
697 unsigned long long n_unitaries = 1ULL << n_controls_;
698 std::vector<Eigen::Matrix2cd> unitaries(n_unitaries);
700 for (
unsigned long long i = 0; i < n_unitaries; i++) {
701 auto it = op_map_.find(
dec_to_bin(i, n_controls_));
702 if (it == op_map_.end()) {
703 unitaries[i] = Eigen::Matrix2cd::Identity();
706 std::shared_ptr<const Unitary1qBox> u1box =
707 std::dynamic_pointer_cast<const Unitary1qBox>(it->second);
708 unitaries[i] = u1box->get_matrix();
710 if (!it->second->free_symbols().empty()) {
711 throw Unsupported(
"Can't decompose symbolic MultiplexedU2Box.");
719 std::vector<std::vector<double>> ucrzs(n_controls_);
720 for (
unsigned i = 0; i < n_controls_; i++) {
721 ucrzs[i] = std::vector<double>(1ULL << (i + 1), 0.0);
724 std::vector<GateSpec> commands = {};
726 recursive_demultiplex_u2(
727 unitaries, n_controls_ + 1, commands, phase, ucrzs,
728 Eigen::Matrix2cd::Identity(), Eigen::Matrix2cd::Identity());
730 Eigen::VectorXcd diag =
731 Eigen::VectorXcd::Constant(1ULL << (n_controls_ + 1), 1);
732 for (
unsigned i = 0; i < n_controls_; i++) {
736 for (
unsigned long long offset = 0;
737 offset < (1ULL << (n_controls_ + 1 - (i + 2))); offset++) {
738 for (
unsigned long long j = 0; j < (1ULL << (i + 1)); j++) {
741 unsigned long long diag_idx =
742 (j >= (1ULL << i)) ? (j - (1ULL << i)) * 2 + 1 : j * 2;
743 diag[diag_idx + offset * (1ULL << (i + 2))] *=
744 std::exp(-0.5 *
i_ *
PI * ucrzs[i][j]);
745 diag[diag_idx + offset * (1ULL << (i + 2)) + (1ULL << (i + 1))] *=
746 std::exp(0.5 *
i_ *
PI * ucrzs[i][j]);
755 Eigen::VectorXcd diag_vec;
757 if (n_controls_ == 0) {
758 auto it = op_map_.begin();
759 circ.
add_op<
unsigned>(it->second, {0});
760 circ_ = std::make_shared<Circuit>(circ);
765 for (
unsigned i = 0; i < decomp.
commands.size(); i++) {
784 (decomp.
diag - Eigen::VectorXcd::Constant(1ULL << circ.
n_qubits(), 1))
787 std::vector<unsigned> args(circ.
n_qubits());
788 std::iota(std::begin(args), std::end(args), 0);
791 circ_ = std::make_shared<Circuit>(circ);
797 auto it = op_map.begin();
798 if (it == op_map.end()) {
799 throw std::invalid_argument(
800 "The op_map argument passed to MultiplexedTensoredU2Box cannot be "
803 n_controls_ = (unsigned)it->first.size();
804 n_targets_ = (unsigned)it->second.size();
805 if (n_controls_ > MAX_N_CONTROLS) {
806 throw std::invalid_argument(
807 "MultiplexedTensoredU2Box only supports bitstrings up to " +
808 std::to_string(MAX_N_CONTROLS) +
" bits.");
810 for (; it != op_map.end(); it++) {
811 if (it->first.size() != n_controls_) {
812 throw std::invalid_argument(
813 "The bitstrings passed to MultiplexedTensoredU2Box must have the "
817 if (it->second.size() != n_targets_) {
818 throw std::invalid_argument(
819 "Each tensored operation passed to MultiplexedTensoredU2Box must "
820 "have the same number of U2 components");
822 for (
auto op : it->second) {
823 OpType optype = op->get_type();
827 "Ops passed to MultiplexedTensoredU2Box must be single-qubit "
828 "unitary gate types or Unitary1qBox.",
838 n_controls_(other.n_controls_),
839 n_targets_(other.n_targets_),
840 op_map_(other.op_map_) {}
843 const SymEngine::map_basic_basic &sub_map)
const {
845 for (
auto it = op_map_.begin(); it != op_map_.end(); it++) {
846 std::vector<Op_ptr> ops;
847 for (
auto op : it->second) {
848 ops.push_back(op->symbol_substitution(sub_map));
850 new_op_map.insert({it->first, ops});
852 return std::make_shared<MultiplexedTensoredU2Box>(new_op_map);
857 for (
auto it = op_map_.begin(); it != op_map_.end(); it++) {
858 for (
auto op : it->second) {
859 SymSet op_symbols = op->free_symbols();
860 all_symbols.insert(op_symbols.begin(), op_symbols.end());
868 for (
auto it = op_map_.begin(); it != op_map_.end(); it++) {
869 std::vector<Op_ptr> ops;
870 for (
auto op : it->second) {
871 ops.push_back(op->dagger());
873 new_op_map.insert({it->first, ops});
875 return std::make_shared<MultiplexedTensoredU2Box>(new_op_map);
880 for (
auto it = op_map_.begin(); it != op_map_.end(); it++) {
881 std::vector<Op_ptr> ops;
882 for (
auto op : it->second) {
883 ops.push_back(op->transpose());
885 new_op_map.insert({it->first, ops});
887 return std::make_shared<MultiplexedTensoredU2Box>(new_op_map);
899 return opmap_compare(op_map_, other.op_map_);
905 j[
"op_map"] = box.get_op_map();
914 boost::lexical_cast<boost::uuids::uuid>(j.at(
"id").get<std::string>()));
918 Circuit &circ,
const std::vector<MultiplexedU2Commands> &m_u2_decomps,
919 unsigned n_controls_,
unsigned n_targets_) {
920 TKET_ASSERT(m_u2_decomps.size() == n_targets_);
923 unsigned reference_size = m_u2_decomps[0].commands.size();
924 for (
unsigned i = 0; i < m_u2_decomps.size(); i++) {
928 TKET_ASSERT(reference_size == m_u2_decomps[i].commands.size());
933 for (
unsigned i = 0; i < reference_size; i++) {
934 for (
unsigned target = 0; target < m_u2_decomps.size(); target++) {
935 GateSpec gate = m_u2_decomps[target].commands[i];
936 unsigned rotated_index;
942 rotated_index = (*gate.
qubit + (target % n_targets_)) % n_controls_;
943 TKET_ASSERT(i % 2 == 1);
945 OpType::CX, {rotated_index, n_controls_ + target});
948 TKET_ASSERT(i % 2 == 0);
960std::pair<ctrl_op_map_t, Eigen::VectorXcd>
962 const Eigen::VectorXcd &full_diag,
unsigned n_controls_) {
965 Eigen::VectorXcd diag_vec =
966 Eigen::VectorXcd::Constant(1ULL << n_controls_, 1);
968 for (
unsigned long long j = 0; j < (1ULL << n_controls_); j++) {
974 Complex b = full_diag[2 * j + 1];
976 double a_phase = std::arg(a);
977 double b_phase = std::arg(b);
978 double alpha = (b_phase - a_phase) /
PI;
979 Complex p = std::exp((b_phase + a_phase) * 0.5 *
i_);
980 if (std::abs(alpha) >
EPS) {
989 return std::make_pair(multip_rz, diag_vec);
993 Circuit &circ,
const std::vector<ctrl_op_map_t> &all_multiplexed_rz,
994 unsigned n_controls_,
unsigned n_targets_) {
995 TKET_ASSERT(all_multiplexed_rz.size() == n_targets_);
996 std::vector<std::vector<GateSpec>> all_decomps;
999 for (
unsigned target = 0; target < n_targets_; target++) {
1002 all_decomps.push_back(
1005 all_decomps.push_back({});
1008 TKET_ASSERT(!all_decomps.empty());
1009 unsigned reference_size = 0;
1010 for (
unsigned i = 0; i < all_decomps.size(); i++) {
1011 if (!all_decomps[i].empty() && !reference_size) {
1012 reference_size = all_decomps[i].size();
1014 TKET_ASSERT(reference_size == all_decomps[i].size());
1018 if (!reference_size)
return;
1022 for (
unsigned i = 0; i < reference_size; i++) {
1023 for (
unsigned target = 0; target < all_decomps.size(); target++) {
1024 if (!all_decomps[target].empty()) {
1025 GateSpec gate = all_decomps[target][i];
1026 unsigned rotated_index;
1027 switch (gate.
type) {
1032 rotated_index = (*gate.
qubit + (target % n_targets_)) % n_controls_;
1034 OpType::CX, {rotated_index, n_controls_ + target});
1051 const std::vector<Eigen::VectorXcd> &all_diags,
unsigned n_controls_,
1052 unsigned n_targets_) {
1053 Eigen::VectorXcd combined_diag_vec =
1054 Eigen::VectorXcd::Constant(1ULL << n_controls_, 1);
1055 TKET_ASSERT(all_diags.size() == n_targets_);
1056 for (
unsigned rotate = 0; rotate < n_targets_; rotate++) {
1057 Eigen::VectorXcd diag_vec = all_diags[rotate];
1058 TKET_ASSERT(diag_vec.size() == combined_diag_vec.size());
1062 for (
unsigned index = 0; index < diag_vec.size(); index++) {
1066 unsigned rotate_value = rotate % n_controls_;
1067 std::vector<bool> as_bits =
dec_to_bin(index, n_controls_);
1070 as_bits.begin(), as_bits.begin() + (as_bits.size() - rotate_value),
1072 unsigned rotated_index =
bin_to_dec(as_bits);
1073 TKET_ASSERT(rotated_index <= combined_diag_vec.size());
1076 combined_diag_vec[rotated_index] *= diag_vec[index];
1079 return combined_diag_vec;
1092 std::vector<MultiplexedU2Commands> m_u2_decomps;
1093 for (
unsigned target = 0; target < n_targets_; target++) {
1095 for (
auto it = op_map_.begin(); it != op_map_.end(); it++) {
1099 std::vector<bool> control_condition = it->first;
1101 control_condition.begin(),
1102 control_condition.begin() + (target % n_controls_),
1103 control_condition.end());
1104 u2_op_map.insert({control_condition, it->second[target]});
1112 std::vector<ctrl_op_map_t> all_multiplexed_rz;
1113 std::vector<Eigen::VectorXcd> all_diags;
1114 for (
unsigned i = 0; i < m_u2_decomps.size(); i++) {
1115 std::pair<ctrl_op_map_t, Eigen::VectorXcd> disentangled =
1117 m_u2_decomps[i].diag, n_controls_);
1118 all_multiplexed_rz.push_back(disentangled.first);
1119 all_diags.push_back(disentangled.second);
1123 Eigen::VectorXcd combined_diag_vec =
1128 Circuit circ(n_controls_ + n_targets_);
1129 add_cx_u1(circ, m_u2_decomps, n_controls_, n_targets_);
1132 add_multi_rz(circ, all_multiplexed_rz, n_controls_, n_targets_);
1135 if ((combined_diag_vec - Eigen::VectorXcd::Constant(1ULL << n_controls_, 1))
1138 std::vector<unsigned> control_qubits(n_controls_);
1139 std::iota(std::begin(control_qubits), std::end(control_qubits), 0);
1142 circ_ = std::make_shared<Circuit>(circ);
Generally useful typedefs and constants.
#define REGISTER_OPFACTORY(type, opclass)
When an OpType needs custom JSON conversion methods (as is the case for box types),...
Operation type not valid in the current context.
Abstract class for an operation from which a circuit can be extracted.
friend Op_ptr set_box_id(BoxT &b, boost::uuids::uuid newid)
Set explicit ID on a box.
std::shared_ptr< Circuit > circ_
boost::uuids::uuid get_id() const
Unique identifier (preserved on copy)
unsigned n_qubits() const
Vertex add_op(const Op_ptr &op, const std::vector< ID > &args, std::optional< std::string > opgroup=std::nullopt)
Append an operation to the circuit.
Vertex add_box(const BoxT &box, const std::vector< ID > &args, std::optional< std::string > opgroup=std::nullopt)
Append a box to the circuit.
void add_phase(Expr a)
Adds a global phase to the circuit.
Box to synthesise a diagonal operator.
Multiplexed single-axis rotations.
Op_ptr symbol_substitution(const SymEngine::map_basic_basic &sub_map) const override
Operation with values for symbols substituted.
SymSet free_symbols() const override
Set of all free symbols occurring in operation parameters.
Op_ptr transpose() const override
Transpose of a unitary operation.
void generate_circuit() const override
Implement multiplexed rotation (i.e.
static Op_ptr from_json(const nlohmann::json &j)
MultiplexedRotationBox(const ctrl_op_map_t &op_map)
Construct from a op_map.
op_signature_t get_signature() const override
Vector specifying type of data for each port on op.
std::vector< GateSpec > decompose() const
Op_ptr dagger() const override
Inverse (of a unitary operation)
static nlohmann::json to_json(const Op_ptr &op)
bool is_equal(const Op &op_other) const override
Equality check between two MultiplexedRotationBox instances.
Multiplexed-Tensored-U2 gate.
static Op_ptr from_json(const nlohmann::json &j)
bool is_equal(const Op &op_other) const override
Equality check between two MultiplexedTensoredU2Box instances.
op_signature_t get_signature() const override
Vector specifying type of data for each port on op.
MultiplexedTensoredU2Box(const ctrl_tensored_op_map_t &op_map)
Construct from a op_map.
Op_ptr symbol_substitution(const SymEngine::map_basic_basic &sub_map) const override
Operation with values for symbols substituted.
static nlohmann::json to_json(const Op_ptr &op)
Op_ptr transpose() const override
Transpose of a unitary operation.
SymSet free_symbols() const override
Set of all free symbols occurring in operation parameters.
Op_ptr dagger() const override
Inverse (of a unitary operation)
void generate_circuit() const override
Implement multiplexed-tensored-U2 gate by decomposing a sequence of MultiplexedU2 gate and moving the...
static Op_ptr from_json(const nlohmann::json &j)
Op_ptr transpose() const override
Transpose of a unitary operation.
Op_ptr dagger() const override
Inverse (of a unitary operation)
SymSet free_symbols() const override
Set of all free symbols occurring in operation parameters.
void generate_circuit() const override
Implement multiplexed U2 gate (i.e.
bool is_equal(const Op &op_other) const override
Equality check between two MultiplexedU2Box instances.
static nlohmann::json to_json(const Op_ptr &op)
op_signature_t get_signature() const override
Vector specifying type of data for each port on op.
MultiplexedU2Commands decompose() const
Decompose the multiplexor into a sequence of interleaving CX and single qubit gates followed by a dia...
MultiplexedU2Box(const ctrl_op_map_t &op_map, bool impl_diag=true)
Construct from a op_map.
Op_ptr symbol_substitution(const SymEngine::map_basic_basic &sub_map) const override
Operation with values for symbols substituted.
void generate_circuit() const override
Implement the multiplexor naively using X gates and QControlBoxes.
Op_ptr dagger() const override
Inverse (of a unitary operation)
Op_ptr transpose() const override
Transpose of a unitary operation.
static nlohmann::json to_json(const Op_ptr &op)
static Op_ptr from_json(const nlohmann::json &j)
op_signature_t get_signature() const override
Vector specifying type of data for each port on op.
SymSet free_symbols() const override
Set of all free symbols occurring in operation parameters.
bool is_equal(const Op &op_other) const override
Equality check between two MultiplexorBox instances.
Op_ptr symbol_substitution(const SymEngine::map_basic_basic &sub_map) const override
Operation with values for symbols substituted.
Abstract class representing an operation type.
One-qubit operation defined as a unitary matrix.
Defines tket::DeviceCharacterisation, used in NoiseAwarePlacement and in commute_SQ_gates_through_SWA...
bool is_single_qubit_unitary_type(OpType optype)
Test for single-qubit gates that can be expressed as TK1.
Gate_ptr as_gate_ptr(Op_ptr op)
Cast a general Op (of gate type) to a Gate.
OpType
Named operation types.
@ QControlBox
See QControlBox.
@ Unitary1qBox
See Unitary1qBox.
@ CX
Controlled OpType::X.
Op_ptr get_op_ptr(OpType chosen_type, const Expr ¶m, unsigned n_qubits)
Get an operation with a given type, single parameter and qubit count.
std::vector< double > tk1_angles_from_unitary(const Eigen::Matrix2cd &U)
Construct TK1 angles and phase from matrix.
constexpr Complex i_(0, 1)
A fixed square root of -1.
@ Quantum
A wire carrying quantum information, corresponding to some allocated Qubit.
std::complex< double > Complex
Complex number.
std::vector< bool > dec_to_bin(unsigned long long dec, unsigned width)
convert an unsigned to its binary representation big-endian
Eigen::Matrix2cd get_matrix_from_tk1_angles(std::vector< Expr > params)
Construct matrix from TK1 angles and phase.
std::shared_ptr< const Op > Op_ptr
RecursionNodeType
Indicates whether a recursion step in recursive_demultiplex_rotation is either a left child,...
unsigned long long bin_to_dec(const std::vector< bool > &bin)
convert an bit vector to its decimal representation big-endian
void add_multi_rz(Circuit &circ, const std::vector< ctrl_op_map_t > &all_multiplexed_rz, unsigned n_controls_, unsigned n_targets_)
Eigen::VectorXcd combine_diagonals(const std::vector< Eigen::VectorXcd > &all_diags, unsigned n_controls_, unsigned n_targets_)
void add_cx_u1(Circuit &circ, const std::vector< MultiplexedU2Commands > &m_u2_decomps, unsigned n_controls_, unsigned n_targets_)
std::map< std::vector< bool >, std::vector< Op_ptr > > ctrl_tensored_op_map_t
Map bitstrings to tensored Ops.
constexpr double EPS
Default tolerance for floating-point comparisons.
std::pair< ctrl_op_map_t, Eigen::VectorXcd > disentangle_final_qubit_from_diagonal(const Eigen::VectorXcd &full_diag, unsigned n_controls_)
std::set< Sym, SymCompareLess > SymSet
std::vector< EdgeType > op_signature_t
nlohmann::json core_box_json(const Box &box)
std::map< std::vector< bool >, Op_ptr > ctrl_op_map_t
Map bitstrings to Ops.
std::optional< Eigen::Matrix2cd > matrix
std::optional< unsigned > qubit
std::optional< Expr > angle
static Eigen::MatrixXcd get_unitary(const Gate &gate)
The gate object knows how many qubits, and the (symbolic) parameters.
std::vector< GateSpec > commands