Commit 581a7d40 authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

Work on mirror circuit impl



Fix eigen indexing and add missing u3 inverse

Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 44c0b871
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot,
                                     PauliLabel in_netPauli) {
  auto [theta1, theta2, theta3] = in_rot;

  theta1 = mod_2pi(theta1);
  theta2 = mod_2pi(theta2);
  theta3 = mod_2pi(theta3);
  if (in_netPauli == PauliLabel::X || in_netPauli == PauliLabel::Z) {
    theta2 *= -1.0;
  }
@@ -33,12 +36,12 @@ GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot,
  }

  // if x or y
  if (in_newPauli == PauliLabel::X || in_netPauli == PauliLabel::Y) {
  if (in_newPauli == PauliLabel::X || in_newPauli == PauliLabel::Y) {
    theta1 = -theta1 + M_PI;
    theta2 = theta2 + M_PI;
  }
  // if y or z
  if (in_newPauli == PauliLabel::Y || in_netPauli == PauliLabel::Z) {
  if (in_newPauli == PauliLabel::Y || in_newPauli == PauliLabel::Z) {
    theta1 = theta1 + M_PI;
  }

@@ -46,7 +49,14 @@ GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot,
  theta1 = mod_2pi(theta1);
  theta2 = mod_2pi(theta2);
  theta3 = mod_2pi(theta3);
  return std::make_tuple(theta1, theta2, theta3);
}

GenRot_t invU3Gate(const GenRot_t &in_rot) {
  auto [theta1, theta2, theta3] = in_rot;
  theta1 = mod_2pi(M_PI - theta1);
  theta2 = mod_2pi(-theta2);
  theta3 = mod_2pi(-theta3 + M_PI);
  return std::make_tuple(theta1, theta2, theta3);
}

@@ -226,7 +236,7 @@ Srep_t composeCliffords(const Srep_t &C1, const Srep_t &C2) {
  }

  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);
  u(Eigen::seq(n, 2 * n - 1), Eigen::seq(0, n - 1)) = Mat_t::Identity(n, n);

  Vec_t vec1 = s1.transpose() * p2;
  Mat_t inner = (s2.transpose() * u) * s2;
+21 −2
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@
#include <vector>
#include <string>
#include <unordered_map>

#include <iostream>
namespace qcor {
namespace utils {
// Generalized rotation (3 angles)
@@ -12,6 +12,22 @@ using GenRot_t = std::tuple<double, double, double>;
enum class PauliLabel { I, X, Y, Z };
static inline std::vector<PauliLabel>
    ALL_PAULI_OPS({PauliLabel::I, PauliLabel::X, PauliLabel::Y, PauliLabel::Z});
inline std::ostream &operator<<(std::ostream &out, PauliLabel value) {
  std::string s;
#define PROCESS_VAL(p)                                                         \
  case (p):                                                                    \
    s = #p;                                                                    \
    break;
  switch (value) {
    PROCESS_VAL(PauliLabel::I);
    PROCESS_VAL(PauliLabel::X);
    PROCESS_VAL(PauliLabel::Y);
    PROCESS_VAL(PauliLabel::Z);
  }
#undef PROCESS_VAL

  return out << s.back();
}

// Symplectic matrix and phase vector representations
using Smatrix_t = std::vector<std::vector<int>>;
@@ -26,7 +42,10 @@ using CliffordGateLayer_t =
GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot,
                                     PauliLabel in_newPauli,
                                     PauliLabel in_netPauli);

// makes a compiled version of the inverse of a compiled general unitary
// negate angles for inverse based on central pauli, account for recompiling the
// X(-pi/2) into X(pi/2)
GenRot_t invU3Gate(const GenRot_t &in_rot);
// Creates a dictionary of the symplectic representations of
// Clifford gates.
// Returns a dictionary of (s matrix, phase vector) pairs,
+49 −14
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ getLayer(std::shared_ptr<xacc::CompositeInstruction> circuit, int layerId) {
  std::vector<std::shared_ptr<xacc::Instruction>> result;
  assert(layerId < circuit->depth());
  auto graphView = circuit->toGraph();
  for (int i = 1; i < graphView->order() - 2; i++) {
  for (int i = 1; i < graphView->order() - 1; i++) {
    auto node = graphView->getVertexProperties(i);
    if (node.get<int>("layer") == layerId) {
      result.emplace_back(
@@ -64,7 +64,51 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
        }
        return result;
      };
  for (int layer = 0; layer < in_circuit->depth(); ++layer) {
  
  // in_circuit->as_xacc()->toGraph()->write(std::cout);
  const auto decomposeU3Angle = [](xacc::InstPtr u3_gate) {
    const double theta = InstructionParameterToDouble(u3_gate->getParameter(0));
    const double phi = InstructionParameterToDouble(u3_gate->getParameter(1));
    const double lam = InstructionParameterToDouble(u3_gate->getParameter(2));
    // Convert to 3 rz angles:
    const double theta1 = lam;
    const double theta2 = theta + M_PI;
    const double theta3 = phi + 3.0 * M_PI;
    return std::make_tuple(theta1, theta2, theta3);
  };

  const auto createU3GateFromAngle = [](size_t qubit, double theta1,
                                        double theta2, double theta3) {
    auto gateProvider = xacc::getService<xacc::IRProvider>("quantum");
    return gateProvider->createInstruction(
        "U", {qubit}, {theta2 - M_PI, theta3 - 3.0 * M_PI, theta1});
  };
  const auto d = in_circuit->depth();
  for (int layer = d - 1; layer >= 0; --layer) {
    auto current_layers = getLayer(in_circuit->as_xacc(), layer);
    for (const auto &gate : current_layers) {
      // Only handle "U3" gate for now.
      // TODO: convert all single-qubit gates to U3
      if (gate->name() == "U") {
        const auto u3_angles = decomposeU3Angle(gate);
        const auto [theta1_inv, theta2_inv, theta3_inv] =
            qcor::utils::invU3Gate(u3_angles);
        const size_t qubit = gate->bits()[0];
        in_circuit->addInstruction(
            gateProvider->createInstruction("Rz", {qubit}, {theta3_inv}));
        in_circuit->addInstruction(
            gateProvider->createInstruction("Rx", {qubit}, {M_PI / 2.0}));
        in_circuit->addInstruction(
            gateProvider->createInstruction("Rz", {qubit}, {theta2_inv}));
        in_circuit->addInstruction(
            gateProvider->createInstruction("Rx", {qubit}, {M_PI / 2.0}));
        in_circuit->addInstruction(
            gateProvider->createInstruction("Rz", {qubit}, {theta1_inv}));
      }
    }
  }

  for (int layer = 0; layer < d; ++layer) {
    auto current_layers = getLayer(in_circuit->as_xacc(), layer);
    // New random Pauli layer
    const std::vector<qcor::utils::PauliLabel> new_paulis = [](int nQubits) {
@@ -93,23 +137,14 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
      // TODO: convert all single-qubit gates to U3
      if (gate->name() == "U") {
        const size_t qubit = gate->bits()[0];
        const double theta =
            InstructionParameterToDouble(gate->getParameter(0));
        const double phi = InstructionParameterToDouble(gate->getParameter(1));
        const double lam = InstructionParameterToDouble(gate->getParameter(2));
        // Convert to 3 rz angles:
        const double theta1 = lam;
        const double theta2 = theta + M_PI;
        const double theta3 = phi + 3.0 * M_PI;
        const auto [theta1, theta2, theta3] = decomposeU3Angle(gate);
        // Compute the pseudo_inverse gate:
        const auto [theta1_new, theta2_new, theta3_new] =
            qcor::utils::computeRotationInPauliFrame(
                std::make_tuple(theta1, theta2, theta3), new_paulis[qubit],
                net_paulis[qubit]);

        mirrorCircuit.emplace_back(gateProvider->createInstruction(
            "U", {qubit},
            {theta2_new - M_PI, theta3_new - 3.0 * M_PI, theta1_new}));
        mirrorCircuit.emplace_back(
            createU3GateFromAngle(qubit, theta1_new, theta2_new, theta3_new));
      }
    }
  }
+2 −2
Original line number Diff line number Diff line
@@ -6,6 +6,6 @@ endif()

add_executable(MirrorCircuitTester MirrorCircuitTester.cpp)
add_test(NAME qcor_MirrorCircuitTester COMMAND MirrorCircuitTester)
target_include_directories(MirrorCircuitTester PRIVATE ../../ ../../../base ${XACC_ROOT}/include/gtest)
target_link_libraries(MirrorCircuitTester ${XACC_TEST_LIBRARIES} xacc::xacc xacc::quantum_gate)
target_include_directories(MirrorCircuitTester PRIVATE .. ${XACC_ROOT}/include/gtest)
target_link_libraries(MirrorCircuitTester ${XACC_TEST_LIBRARIES} xacc::xacc xacc::quantum_gate qcor-mirror-rb)
+32 −4
Original line number Diff line number Diff line
@@ -2,12 +2,40 @@
#include "xacc_service.hpp"
#include <gtest/gtest.h>
#include <fstream>


TEST(MirrorCircuitTester, checkSimple) {
  // TODO
#include "mirror_circuit_rb.hpp"
#include "qcor_ir.hpp"
#include <random>
namespace {
double random_angle() {
  static std::uniform_real_distribution<double> dis(-M_PI, M_PI);
  static std::default_random_engine re;
  return dis(re);
}
} // namespace

TEST(MirrorCircuitTester, checkU3Inverse) {
  auto provider = xacc::getIRProvider("quantum");
  constexpr int NUM_TESTS = 1000;
  for (int i = 0; i < NUM_TESTS; ++i) {
    auto circuit = provider->createComposite("test");
    const double theta = random_angle();
    const double phi = random_angle();
    const double lambda = random_angle();
    circuit->addInstruction(provider->createInstruction(
        "U", {0}, std::vector<xacc::InstructionParameter>{theta, phi, lambda}));
    auto [mirror_cir, expected_result] = qcor::createMirrorCircuit(
        std::make_shared<qcor::CompositeInstruction>(circuit));
    auto accelerator = xacc::getAccelerator("qpp", {{"shots", 1024}});
    // circuit->addInstructions(mirror_cir->getInstructions());
    circuit->addInstruction(provider->createInstruction("Measure", {0}));
    // std::cout << "HOWDY: \n" << circuit->toString() << "\n";
    auto buffer = xacc::qalloc(1);
    accelerator->execute(buffer, circuit);
    buffer->print();
    EXPECT_EQ(buffer->getMeasurementCounts().size(), 1);
    EXPECT_EQ(buffer->getMeasurementCounts()["0"], 1024);
  }
}

int main(int argc, char **argv) {
  xacc::Initialize();