Commit 44c0b871 authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

Port the main mirror circuit gen



Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 508a3e1e
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -5,7 +5,12 @@ file(GLOB HEADERS *.hpp)

add_library(${LIBRARY_NAME} SHARED ${SRC})
target_include_directories(${LIBRARY_NAME} PUBLIC . ${XACC_ROOT}/include/eigen)
target_link_libraries(${LIBRARY_NAME} PUBLIC qcor qrt)
xacc_configure_library_rpath(${LIBRARY_NAME})

install(FILES ${HEADERS} DESTINATION include/qcor)
install(TARGETS ${LIBRARY_NAME} DESTINATION lib)

if (QCOR_BUILD_TESTS)
  add_subdirectory(tests)
endif()
 No newline at end of file
+24 −6
Original line number Diff line number Diff line
#include "clifford_gate_utils.hpp"
#include <Eigen/Dense>
#include <cassert>
#include <cmath>
#include <set>
#include <Eigen/Dense>

namespace {
double mod_2pi(double theta) {
@@ -21,24 +21,24 @@ namespace qcor {
namespace utils {
GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot,
                                     PauliLabel in_newPauli,
                                     PauliLabel &io_netPauli) {
                                     PauliLabel in_netPauli) {
  auto [theta1, theta2, theta3] = in_rot;

  if (io_netPauli == PauliLabel::X || io_netPauli == PauliLabel::Z) {
  if (in_netPauli == PauliLabel::X || in_netPauli == PauliLabel::Z) {
    theta2 *= -1.0;
  }
  if (io_netPauli == PauliLabel::X || io_netPauli == PauliLabel::Y) {
  if (in_netPauli == PauliLabel::X || in_netPauli == PauliLabel::Y) {
    theta3 *= -1.0;
    theta1 *= -1.0;
  }

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

@@ -297,5 +297,23 @@ Srep_t computeCircuitSymplecticRepresentations(
  }
  return std::make_pair(s, p);
}

std::vector<PauliLabel> find_pauli_labels(const Pvec_t &pvec) {
  assert(pvec.size() % 2 == 0);
  const auto n = pvec.size() / 2;
  std::vector<int> v(n, 0);
  for (int i = 0; i < n; ++i) {
    v[i] = (pvec[i] / 2) + 2 * (pvec[n + i] / 2);
  }
  // [0,0]=I, [2,0]=Z, [0,2]=X, and [2,2]=Y.
  std::vector<PauliLabel> result;
  for (const auto &el : v) {
    assert(el < 4);
    static const std::vector<PauliLabel> ARRAY{PauliLabel::I, PauliLabel::Z,
                                               PauliLabel::X, PauliLabel::Y};
    result.emplace_back(ARRAY[el]);
  }
  return result;
}
} // namespace utils
} // namespace qcor
 No newline at end of file
+6 −1
Original line number Diff line number Diff line
@@ -10,6 +10,9 @@ namespace utils {
// rz - sx - rz - sx - rz
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});

// Symplectic matrix and phase vector representations
using Smatrix_t = std::vector<std::vector<int>>;
using Pvec_t = std::vector<int>;
@@ -22,7 +25,7 @@ using CliffordGateLayer_t =
// - in_pauli: the randomined Pauli op
GenRot_t computeRotationInPauliFrame(const GenRot_t &in_rot,
                                     PauliLabel in_newPauli,
                                     PauliLabel &io_netPauli);
                                     PauliLabel in_netPauli);

// Creates a dictionary of the symplectic representations of
// Clifford gates.
@@ -43,5 +46,7 @@ Srep_t computeCircuitSymplecticRepresentations(
// 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);

std::vector<PauliLabel> find_pauli_labels(const Pvec_t& pvec);
} // namespace utils
} // namespace qcor
 No newline at end of file
+131 −0
Original line number Diff line number Diff line
#include "mirror_circuit_rb.hpp"
#include "clifford_gate_utils.hpp"
#include "qcor_ir.hpp"
#include "qcor_pimpl_impl.hpp"
#include "xacc.hpp"
#include "xacc_service.hpp"
#include <cassert>
#include <random>

namespace {
std::vector<std::shared_ptr<xacc::Instruction>>
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++) {
    auto node = graphView->getVertexProperties(i);
    if (node.get<int>("layer") == layerId) {
      result.emplace_back(
          circuit->getInstruction(node.get<std::size_t>("id") - 1)->clone());
    }
  }
  assert(!result.empty());
  return result;
}
} // namespace

namespace qcor {
std::pair<std::shared_ptr<CompositeInstruction>, std::vector<bool>>
createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
  std::vector<std::shared_ptr<xacc::Instruction>> mirrorCircuit;
  auto gateProvider = xacc::getService<xacc::IRProvider>("quantum");

  const int n = in_circuit->nPhysicalBits();
  // Tracking the Pauli layer as it is commuted through
  std::vector<qcor::utils::PauliLabel> net_paulis(n,
                                                  qcor::utils::PauliLabel::I);

  // Sympletic group for Pauli gates:
  const auto srep_dict =
      qcor::utils::computeGateSymplecticRepresentations({"I", "X", "Y", "Z"});

  const auto pauliListToLayer =
      [](const std::vector<qcor::utils::PauliLabel> &in_paulis) {
        qcor::utils::CliffordGateLayer_t result;
        for (int i = 0; i < in_paulis.size(); ++i) {
          const auto pauli = in_paulis[i];
          switch (pauli) {
          case qcor::utils::PauliLabel::I:
            result.emplace_back(std::make_pair("I", i));
            break;
          case qcor::utils::PauliLabel::X:
            result.emplace_back(std::make_pair("X", i));
            break;
          case qcor::utils::PauliLabel::Y:
            result.emplace_back(std::make_pair("Y", i));
            break;
          case qcor::utils::PauliLabel::Z:
            result.emplace_back(std::make_pair("Z", i));
            break;
          default:
            __builtin_unreachable();
          }
        }
        return result;
      };
  for (int layer = 0; layer < in_circuit->depth(); ++layer) {
    auto current_layers = getLayer(in_circuit->as_xacc(), layer);
    // New random Pauli layer
    const std::vector<qcor::utils::PauliLabel> new_paulis = [](int nQubits) {
      static std::random_device rd;
      static std::mt19937 gen(rd());
      static std::uniform_int_distribution<size_t> dis(
          0, qcor::utils::ALL_PAULI_OPS.size() - 1);
      std::vector<qcor::utils::PauliLabel> random_paulis;
      for (int i = 0; i < nQubits; ++i) {
        random_paulis.emplace_back(qcor::utils::ALL_PAULI_OPS[dis(gen)]);
      }

      return random_paulis;
    }(n);

    const auto new_paulis_as_layer = pauliListToLayer(new_paulis);
    const auto current_net_paulis_as_layer = pauliListToLayer(net_paulis);
    const auto new_net_paulis_reps =
        qcor::utils::computeCircuitSymplecticRepresentations(
            {new_paulis_as_layer, current_net_paulis_as_layer}, n, srep_dict);

    // Update the tracking net
    net_paulis = qcor::utils::find_pauli_labels(new_net_paulis_reps.second);
    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 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;
        // 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}));
      }
    }
  }

  const auto [telp_s, telp_p] =
      qcor::utils::computeLayerSymplecticRepresentations(
          pauliListToLayer(net_paulis), n, srep_dict);
  std::vector<bool> target_bitString;
  for (int i = n; i < telp_p.size(); ++i) {
    target_bitString.emplace_back(telp_p[i] == 2);
  }

  auto mirror_comp =
      gateProvider->createComposite(in_circuit->name() + "_MIRROR");
  mirror_comp->addInstructions(mirrorCircuit);
  return std::make_pair(std::make_shared<CompositeInstruction>(mirror_comp),
                        target_bitString);
}
} // namespace qcor
+13 −0
Original line number Diff line number Diff line
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <utility>

// !Temporary: we'll figure out an interface for this later:
namespace qcor {
class CompositeInstruction;
// Return the 'mirrored' circuit along with the expected result.
std::pair<std::shared_ptr<CompositeInstruction>, std::vector<bool>>
createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit);
} // namespace qcor
Loading