Loading lib/mirror_rb/CMakeLists.txt +1 −3 Original line number Diff line number Diff line Loading @@ -4,9 +4,7 @@ file(GLOB SRC *.cpp) file(GLOB HEADERS *.hpp) add_library(${LIBRARY_NAME} SHARED ${SRC}) target_link_libraries(${LIBRARY_NAME} PUBLIC qcor) target_include_directories(${LIBRARY_NAME} PUBLIC . ${XACC_ROOT}/include/eigen) xacc_configure_library_rpath(${LIBRARY_NAME}) install(FILES ${HEADERS} DESTINATION include/qcor) Loading lib/mirror_rb/clifford_gate_utils.cpp +251 −0 Original line number Diff line number Diff line #include "clifford_gate_utils.hpp" #include <cassert> #include <cmath> #include <set> #include <Eigen/Dense> namespace { double mod_2pi(double theta) { while (theta > M_PI or theta <= -M_PI) { Loading Loading @@ -46,5 +49,253 @@ GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot, return std::make_tuple(theta1, theta2, theta3); } SrepDict_t computeGateSymplecticRepresentations( const std::vector<std::string> &in_gateList) { static const SrepDict_t standardDict = []() { std::unordered_map<std::string, Smatrix_t> complete_s_dict; std::unordered_map<std::string, Pvec_t> complete_p_dict; // The Pauli gates complete_s_dict["I"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_s_dict["X"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_s_dict["Y"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_s_dict["Z"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["I"] = std::vector<int>{0, 0}; complete_p_dict["X"] = std::vector<int>{0, 2}; complete_p_dict["Y"] = std::vector<int>{2, 2}; complete_p_dict["Z"] = std::vector<int>{2, 0}; // Five single qubit gates that each represent one of five classes of // Cliffords that equivalent up to Pauli gates and are not equivalent to // idle (that class is covered by any one of the Pauli gates above). complete_s_dict["H"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_s_dict["P"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_s_dict["PH"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_s_dict["HP"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_s_dict["HPH"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["H"] = std::vector<int>{0, 0}; complete_p_dict["P"] = std::vector<int>{1, 0}; complete_p_dict["PH"] = std::vector<int>{0, 1}; complete_p_dict["HP"] = std::vector<int>{3, 0}; complete_p_dict["HPH"] = std::vector<int>{0, 3}; // The full 1-qubit Cliffor group, using the same labelling as in // extras.rb.group complete_s_dict["C0"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C0"] = std::vector<int>{0, 0}; complete_s_dict["C1"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C1"] = std::vector<int>{1, 0}; complete_s_dict["C2"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C2"] = std::vector<int>{0, 1}; complete_s_dict["C3"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C3"] = std::vector<int>{0, 2}; complete_s_dict["C4"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C4"] = std::vector<int>{1, 2}; complete_s_dict["C5"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C5"] = std::vector<int>{0, 3}; complete_s_dict["C6"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C6"] = std::vector<int>{2, 2}; complete_s_dict["C7"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C7"] = std::vector<int>{3, 2}; complete_s_dict["C8"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C8"] = std::vector<int>{2, 3}; complete_s_dict["C9"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C9"] = std::vector<int>{2, 0}; complete_s_dict["C10"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C10"] = std::vector<int>{3, 0}; complete_s_dict["C11"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C11"] = std::vector<int>{2, 1}; complete_s_dict["C12"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C12"] = std::vector<int>{0, 0}; complete_s_dict["C13"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C13"] = std::vector<int>{0, 1}; complete_s_dict["C14"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C14"] = std::vector<int>{1, 0}; complete_s_dict["C15"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C15"] = std::vector<int>{0, 2}; complete_s_dict["C16"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C16"] = std::vector<int>{0, 3}; complete_s_dict["C17"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C17"] = std::vector<int>{1, 2}; complete_s_dict["C18"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C18"] = std::vector<int>{2, 2}; complete_s_dict["C19"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C19"] = std::vector<int>{2, 3}; complete_s_dict["C20"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C20"] = std::vector<int>{3, 2}; complete_s_dict["C21"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C21"] = std::vector<int>{2, 0}; complete_s_dict["C22"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C22"] = std::vector<int>{2, 1}; complete_s_dict["C23"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C23"] = std::vector<int>{3, 0}; // The CNOT gate, CPHASE gate, and SWAP gate. complete_s_dict["CNOT"] = std::vector<std::vector<int>>{ {1, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 1, 1}, {0, 0, 0, 1}}; complete_s_dict["CPHASE"] = std::vector<std::vector<int>>{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 1, 1, 0}, {1, 0, 0, 1}}; complete_s_dict["SWAP"] = std::vector<std::vector<int>>{ {0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; complete_p_dict["CNOT"] = std::vector<int>{0, 0, 0, 0}; complete_p_dict["CPHASE"] = std::vector<int>{0, 0, 0, 0}; complete_p_dict["SWAP"] = std::vector<int>{0, 0, 0, 0}; SrepDict_t result; for (const auto &[k, v] : complete_s_dict) { assert(complete_p_dict.find(k) != complete_p_dict.end()); result[k] = std::make_pair(v, complete_p_dict[k]); } return result; }(); if (!in_gateList.empty()) { // Filter a subset of gates: SrepDict_t result; for (const auto &gateLabel : in_gateList) { const auto iter = standardDict.find(gateLabel); assert(iter != standardDict.end()); result[gateLabel] = iter->second; } return result; } return standardDict; } Srep_t computeLayerSymplecticRepresentations(const CliffordGateLayer_t &layers, int nQubits, const SrepDict_t &srep_dict) { // Initilize Pvec_t p(2 * nQubits, 0); Smatrix_t s(2 * nQubits, p); std::set<int> seen_qubits; for (const auto &[name, operands] : layers) { const auto iter = srep_dict.find(name); assert(iter != srep_dict.end()); const auto &[matrix, phase] = iter->second; const auto nforgate = operands.size(); for (int ind1 = 0; ind1 < operands.size(); ++ind1) { const auto qindex1 = operands[ind1]; assert(seen_qubits.find(qindex1) == seen_qubits.end()); seen_qubits.emplace(qindex1); for (int ind2 = 0; ind2 < operands.size(); ++ind2) { const auto qindex2 = operands[ind2]; // Put in the symp matrix elements s[qindex1][qindex2] = matrix[ind1][ind2]; s[qindex1][qindex2 + nQubits] = matrix[ind1][ind2 + nforgate]; s[qindex1 + nQubits][qindex2] = matrix[ind1 + nforgate][ind2]; s[qindex1 + nQubits][qindex2 + nQubits] = matrix[ind1 + nforgate][ind2 + nforgate]; } // Put in the phase elements p[qindex1] = phase[ind1]; p[qindex1 + nQubits] = phase[ind1 + nforgate]; } } return std::make_pair(s, p); } Srep_t composeCliffords(const Srep_t &C1, const Srep_t &C2) { assert(C1.first.size() == C2.first.size()); assert(C1.second.size() == C2.second.size()); assert(C1.first.size() % 2 == 0); const int n = C1.first.size() / 2; using Mat_t = Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>; using Vec_t = Eigen::Matrix<int, Eigen::Dynamic, 1>; Mat_t s1(2 * n, 2 * n); Mat_t s2(2 * n, 2 * n); Vec_t p1(2 * n); Vec_t p2(2 * n); // Load to eigen for processing for (int i = 0; i < 2 * n; ++i) { for (int j = 0; j < 2 * n; ++j) { s1(i, j) = C1.first[i][j]; s2(i, j) = C2.first[i][j]; } p1(i) = C1.second[i]; p2(i) = C2.second[i]; } Mat_t s = s2 * s1; // Mod 2 for (int i = 0; i < 2 * n; ++i) { for (int j = 0; j < 2 * n; ++j) { s(i, j) = s(i, j) % 2; } } Mat_t u = Mat_t::Zero(2 * n, 2 * n); u(Eigen::seq(n, 2 * n), Eigen::seq(0, n)) = Mat_t::Identity(n, n); Vec_t vec1 = s1.transpose() * p2; Mat_t inner = (s2.transpose() * u) * s2; const auto strictly_upper_triangle = [](const Mat_t &m) -> Mat_t { auto l = m.rows(); Mat_t out = m; for (int i = 0; i < l; ++i) { for (int j = 0; j < i + 1; ++j) { out(i, j) = 0; } } return out; }; // Returns a diagonal matrix containing the diagonal of m. const auto diagonal_as_matrix = [](const Mat_t &m) -> Mat_t { auto l = m.rows(); Mat_t out = Mat_t::Zero(l, l); for (int i = 0; i < l; ++i) { out(i, i) = m(i, i); } return out; }; // Returns a 1D array containing the diagonal of the input square 2D array m. const auto diagonal_as_vec = [](const Mat_t &m) -> Mat_t { auto l = m.rows(); Vec_t vec = Vec_t::Zero(l); for (int i = 0; i < l; ++i) { vec(i) = m(i, i); } return vec; }; Mat_t matrix = 2 * strictly_upper_triangle(inner) + diagonal_as_matrix(inner); Vec_t vec2 = diagonal_as_vec((s1.transpose() * matrix) * s1); Vec_t vec3 = s1.transpose() * diagonal_as_vec(inner); Vec_t p = p1 + vec1 + vec2 - vec3; for (int i = 0; i < p.size(); ++i) { p(i) = p(i) % 4; } Pvec_t p_res(2 * n, 0); Smatrix_t s_res(2 * n, p_res); for (int i = 0; i < 2 * n; ++i) { for (int j = 0; j < 2 * n; ++j) { s_res[i][j] = s(i, j); } p_res[i] = p(i); } return std::make_pair(s_res, p_res); } Srep_t computeCircuitSymplecticRepresentations( const std::vector<CliffordGateLayer_t> &layers, int nQubits, const SrepDict_t &srep_dict) { // Initilize Pvec_t p(2 * nQubits, 0); Smatrix_t s(2 * nQubits, p); for (const auto &layer : layers) { const auto layerRep = computeLayerSymplecticRepresentations(layer, nQubits, srep_dict); std::tie(s, p) = composeCliffords(std::make_pair(s, p), layerRep); } return std::make_pair(s, p); } } // namespace utils } // namespace qcor No newline at end of file lib/mirror_rb/clifford_gate_utils.hpp +32 −0 Original line number Diff line number Diff line #pragma once #include <tuple> #include <vector> #include <string> #include <unordered_map> namespace qcor { namespace utils { // Generalized rotation (3 angles) // rz - sx - rz - sx - rz using GenRot_t = std::tuple<double, double, double>; enum class PauliLabel { I, X, Y, Z }; // Symplectic matrix and phase vector representations using Smatrix_t = std::vector<std::vector<int>>; using Pvec_t = std::vector<int>; using Srep_t = std::pair<Smatrix_t, Pvec_t>; using SrepDict_t = std::unordered_map<std::string, Srep_t>; // List of gate label and operands using CliffordGateLayer_t = std::vector<std::pair<std::string, std::vector<int>>>; // Computes z rotation angles in a randomized Pauli frame. // - in_pauli: the randomined Pauli op GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot, PauliLabel in_newPauli, PauliLabel &io_netPauli); // Creates a dictionary of the symplectic representations of // Clifford gates. // Returns a dictionary of (s matrix, phase vector) pairs, // i.e., symplectic matrix and phase vector representing the operation label // given by the key. SrepDict_t computeGateSymplecticRepresentations( const std::vector<std::string> &in_gateList = {}); Srep_t computeLayerSymplecticRepresentations(const CliffordGateLayer_t &layers, int nQubits, const SrepDict_t &srep_dict); Srep_t computeCircuitSymplecticRepresentations( const std::vector<CliffordGateLayer_t> &layers, int nQubits, const SrepDict_t &srep_dict); // Multiplies two cliffords in the symplectic representation. // C2 times C1 (i.e., C1 acts first) Srep_t composeCliffords(const Srep_t &C1, const Srep_t &C2); } // namespace utils } // namespace qcor No newline at end of file Loading
lib/mirror_rb/CMakeLists.txt +1 −3 Original line number Diff line number Diff line Loading @@ -4,9 +4,7 @@ file(GLOB SRC *.cpp) file(GLOB HEADERS *.hpp) add_library(${LIBRARY_NAME} SHARED ${SRC}) target_link_libraries(${LIBRARY_NAME} PUBLIC qcor) target_include_directories(${LIBRARY_NAME} PUBLIC . ${XACC_ROOT}/include/eigen) xacc_configure_library_rpath(${LIBRARY_NAME}) install(FILES ${HEADERS} DESTINATION include/qcor) Loading
lib/mirror_rb/clifford_gate_utils.cpp +251 −0 Original line number Diff line number Diff line #include "clifford_gate_utils.hpp" #include <cassert> #include <cmath> #include <set> #include <Eigen/Dense> namespace { double mod_2pi(double theta) { while (theta > M_PI or theta <= -M_PI) { Loading Loading @@ -46,5 +49,253 @@ GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot, return std::make_tuple(theta1, theta2, theta3); } SrepDict_t computeGateSymplecticRepresentations( const std::vector<std::string> &in_gateList) { static const SrepDict_t standardDict = []() { std::unordered_map<std::string, Smatrix_t> complete_s_dict; std::unordered_map<std::string, Pvec_t> complete_p_dict; // The Pauli gates complete_s_dict["I"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_s_dict["X"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_s_dict["Y"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_s_dict["Z"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["I"] = std::vector<int>{0, 0}; complete_p_dict["X"] = std::vector<int>{0, 2}; complete_p_dict["Y"] = std::vector<int>{2, 2}; complete_p_dict["Z"] = std::vector<int>{2, 0}; // Five single qubit gates that each represent one of five classes of // Cliffords that equivalent up to Pauli gates and are not equivalent to // idle (that class is covered by any one of the Pauli gates above). complete_s_dict["H"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_s_dict["P"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_s_dict["PH"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_s_dict["HP"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_s_dict["HPH"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["H"] = std::vector<int>{0, 0}; complete_p_dict["P"] = std::vector<int>{1, 0}; complete_p_dict["PH"] = std::vector<int>{0, 1}; complete_p_dict["HP"] = std::vector<int>{3, 0}; complete_p_dict["HPH"] = std::vector<int>{0, 3}; // The full 1-qubit Cliffor group, using the same labelling as in // extras.rb.group complete_s_dict["C0"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C0"] = std::vector<int>{0, 0}; complete_s_dict["C1"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C1"] = std::vector<int>{1, 0}; complete_s_dict["C2"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C2"] = std::vector<int>{0, 1}; complete_s_dict["C3"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C3"] = std::vector<int>{0, 2}; complete_s_dict["C4"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C4"] = std::vector<int>{1, 2}; complete_s_dict["C5"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C5"] = std::vector<int>{0, 3}; complete_s_dict["C6"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C6"] = std::vector<int>{2, 2}; complete_s_dict["C7"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C7"] = std::vector<int>{3, 2}; complete_s_dict["C8"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C8"] = std::vector<int>{2, 3}; complete_s_dict["C9"] = std::vector<std::vector<int>>{{1, 0}, {0, 1}}; complete_p_dict["C9"] = std::vector<int>{2, 0}; complete_s_dict["C10"] = std::vector<std::vector<int>>{{1, 1}, {1, 0}}; complete_p_dict["C10"] = std::vector<int>{3, 0}; complete_s_dict["C11"] = std::vector<std::vector<int>>{{0, 1}, {1, 1}}; complete_p_dict["C11"] = std::vector<int>{2, 1}; complete_s_dict["C12"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C12"] = std::vector<int>{0, 0}; complete_s_dict["C13"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C13"] = std::vector<int>{0, 1}; complete_s_dict["C14"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C14"] = std::vector<int>{1, 0}; complete_s_dict["C15"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C15"] = std::vector<int>{0, 2}; complete_s_dict["C16"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C16"] = std::vector<int>{0, 3}; complete_s_dict["C17"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C17"] = std::vector<int>{1, 2}; complete_s_dict["C18"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C18"] = std::vector<int>{2, 2}; complete_s_dict["C19"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C19"] = std::vector<int>{2, 3}; complete_s_dict["C20"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C20"] = std::vector<int>{3, 2}; complete_s_dict["C21"] = std::vector<std::vector<int>>{{0, 1}, {1, 0}}; complete_p_dict["C21"] = std::vector<int>{2, 0}; complete_s_dict["C22"] = std::vector<std::vector<int>>{{1, 1}, {0, 1}}; complete_p_dict["C22"] = std::vector<int>{2, 1}; complete_s_dict["C23"] = std::vector<std::vector<int>>{{1, 0}, {1, 1}}; complete_p_dict["C23"] = std::vector<int>{3, 0}; // The CNOT gate, CPHASE gate, and SWAP gate. complete_s_dict["CNOT"] = std::vector<std::vector<int>>{ {1, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 1, 1}, {0, 0, 0, 1}}; complete_s_dict["CPHASE"] = std::vector<std::vector<int>>{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 1, 1, 0}, {1, 0, 0, 1}}; complete_s_dict["SWAP"] = std::vector<std::vector<int>>{ {0, 1, 0, 0}, {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; complete_p_dict["CNOT"] = std::vector<int>{0, 0, 0, 0}; complete_p_dict["CPHASE"] = std::vector<int>{0, 0, 0, 0}; complete_p_dict["SWAP"] = std::vector<int>{0, 0, 0, 0}; SrepDict_t result; for (const auto &[k, v] : complete_s_dict) { assert(complete_p_dict.find(k) != complete_p_dict.end()); result[k] = std::make_pair(v, complete_p_dict[k]); } return result; }(); if (!in_gateList.empty()) { // Filter a subset of gates: SrepDict_t result; for (const auto &gateLabel : in_gateList) { const auto iter = standardDict.find(gateLabel); assert(iter != standardDict.end()); result[gateLabel] = iter->second; } return result; } return standardDict; } Srep_t computeLayerSymplecticRepresentations(const CliffordGateLayer_t &layers, int nQubits, const SrepDict_t &srep_dict) { // Initilize Pvec_t p(2 * nQubits, 0); Smatrix_t s(2 * nQubits, p); std::set<int> seen_qubits; for (const auto &[name, operands] : layers) { const auto iter = srep_dict.find(name); assert(iter != srep_dict.end()); const auto &[matrix, phase] = iter->second; const auto nforgate = operands.size(); for (int ind1 = 0; ind1 < operands.size(); ++ind1) { const auto qindex1 = operands[ind1]; assert(seen_qubits.find(qindex1) == seen_qubits.end()); seen_qubits.emplace(qindex1); for (int ind2 = 0; ind2 < operands.size(); ++ind2) { const auto qindex2 = operands[ind2]; // Put in the symp matrix elements s[qindex1][qindex2] = matrix[ind1][ind2]; s[qindex1][qindex2 + nQubits] = matrix[ind1][ind2 + nforgate]; s[qindex1 + nQubits][qindex2] = matrix[ind1 + nforgate][ind2]; s[qindex1 + nQubits][qindex2 + nQubits] = matrix[ind1 + nforgate][ind2 + nforgate]; } // Put in the phase elements p[qindex1] = phase[ind1]; p[qindex1 + nQubits] = phase[ind1 + nforgate]; } } return std::make_pair(s, p); } Srep_t composeCliffords(const Srep_t &C1, const Srep_t &C2) { assert(C1.first.size() == C2.first.size()); assert(C1.second.size() == C2.second.size()); assert(C1.first.size() % 2 == 0); const int n = C1.first.size() / 2; using Mat_t = Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>; using Vec_t = Eigen::Matrix<int, Eigen::Dynamic, 1>; Mat_t s1(2 * n, 2 * n); Mat_t s2(2 * n, 2 * n); Vec_t p1(2 * n); Vec_t p2(2 * n); // Load to eigen for processing for (int i = 0; i < 2 * n; ++i) { for (int j = 0; j < 2 * n; ++j) { s1(i, j) = C1.first[i][j]; s2(i, j) = C2.first[i][j]; } p1(i) = C1.second[i]; p2(i) = C2.second[i]; } Mat_t s = s2 * s1; // Mod 2 for (int i = 0; i < 2 * n; ++i) { for (int j = 0; j < 2 * n; ++j) { s(i, j) = s(i, j) % 2; } } Mat_t u = Mat_t::Zero(2 * n, 2 * n); u(Eigen::seq(n, 2 * n), Eigen::seq(0, n)) = Mat_t::Identity(n, n); Vec_t vec1 = s1.transpose() * p2; Mat_t inner = (s2.transpose() * u) * s2; const auto strictly_upper_triangle = [](const Mat_t &m) -> Mat_t { auto l = m.rows(); Mat_t out = m; for (int i = 0; i < l; ++i) { for (int j = 0; j < i + 1; ++j) { out(i, j) = 0; } } return out; }; // Returns a diagonal matrix containing the diagonal of m. const auto diagonal_as_matrix = [](const Mat_t &m) -> Mat_t { auto l = m.rows(); Mat_t out = Mat_t::Zero(l, l); for (int i = 0; i < l; ++i) { out(i, i) = m(i, i); } return out; }; // Returns a 1D array containing the diagonal of the input square 2D array m. const auto diagonal_as_vec = [](const Mat_t &m) -> Mat_t { auto l = m.rows(); Vec_t vec = Vec_t::Zero(l); for (int i = 0; i < l; ++i) { vec(i) = m(i, i); } return vec; }; Mat_t matrix = 2 * strictly_upper_triangle(inner) + diagonal_as_matrix(inner); Vec_t vec2 = diagonal_as_vec((s1.transpose() * matrix) * s1); Vec_t vec3 = s1.transpose() * diagonal_as_vec(inner); Vec_t p = p1 + vec1 + vec2 - vec3; for (int i = 0; i < p.size(); ++i) { p(i) = p(i) % 4; } Pvec_t p_res(2 * n, 0); Smatrix_t s_res(2 * n, p_res); for (int i = 0; i < 2 * n; ++i) { for (int j = 0; j < 2 * n; ++j) { s_res[i][j] = s(i, j); } p_res[i] = p(i); } return std::make_pair(s_res, p_res); } Srep_t computeCircuitSymplecticRepresentations( const std::vector<CliffordGateLayer_t> &layers, int nQubits, const SrepDict_t &srep_dict) { // Initilize Pvec_t p(2 * nQubits, 0); Smatrix_t s(2 * nQubits, p); for (const auto &layer : layers) { const auto layerRep = computeLayerSymplecticRepresentations(layer, nQubits, srep_dict); std::tie(s, p) = composeCliffords(std::make_pair(s, p), layerRep); } return std::make_pair(s, p); } } // namespace utils } // namespace qcor No newline at end of file
lib/mirror_rb/clifford_gate_utils.hpp +32 −0 Original line number Diff line number Diff line #pragma once #include <tuple> #include <vector> #include <string> #include <unordered_map> namespace qcor { namespace utils { // Generalized rotation (3 angles) // rz - sx - rz - sx - rz using GenRot_t = std::tuple<double, double, double>; enum class PauliLabel { I, X, Y, Z }; // Symplectic matrix and phase vector representations using Smatrix_t = std::vector<std::vector<int>>; using Pvec_t = std::vector<int>; using Srep_t = std::pair<Smatrix_t, Pvec_t>; using SrepDict_t = std::unordered_map<std::string, Srep_t>; // List of gate label and operands using CliffordGateLayer_t = std::vector<std::pair<std::string, std::vector<int>>>; // Computes z rotation angles in a randomized Pauli frame. // - in_pauli: the randomined Pauli op GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot, PauliLabel in_newPauli, PauliLabel &io_netPauli); // Creates a dictionary of the symplectic representations of // Clifford gates. // Returns a dictionary of (s matrix, phase vector) pairs, // i.e., symplectic matrix and phase vector representing the operation label // given by the key. SrepDict_t computeGateSymplecticRepresentations( const std::vector<std::string> &in_gateList = {}); Srep_t computeLayerSymplecticRepresentations(const CliffordGateLayer_t &layers, int nQubits, const SrepDict_t &srep_dict); Srep_t computeCircuitSymplecticRepresentations( const std::vector<CliffordGateLayer_t> &layers, int nQubits, const SrepDict_t &srep_dict); // Multiplies two cliffords in the symplectic representation. // C2 times C1 (i.e., C1 acts first) Srep_t composeCliffords(const Srep_t &C1, const Srep_t &C2); } // namespace utils } // namespace qcor No newline at end of file