Commit 09cc54bc authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

Handle clifford gates



Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent c5e99b56
Loading
Loading
Loading
Loading
+63 −19
Original line number Diff line number Diff line
@@ -36,9 +36,8 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
  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"});
  // Sympletic group 
  const auto srep_dict = qcor::utils::computeGateSymplecticRepresentations();

  const auto pauliListToLayer =
      [](const std::vector<qcor::utils::PauliLabel> &in_paulis) {
@@ -83,12 +82,14 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
    return gateProvider->createInstruction(
        "U", {qubit}, {theta2 - M_PI, theta3 - 3.0 * M_PI, theta1});
  };
  static const std::vector<std::string> SELF_ADJOINT_CLIFFORD_GATES{
      "H", "X", "Y", "Z", "CNOT", "Swap"};
  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
      // TODO: convert all single-qubit rotation gates to U3
      if (gate->name() == "U") {
        const auto u3_angles = decomposeU3Angle(gate);
        const auto [theta1_inv, theta2_inv, theta3_inv] =
@@ -97,6 +98,10 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
        in_circuit->addInstruction(gateProvider->createInstruction(
            "U", {qubit},
            {theta2_inv - M_PI, theta1_inv - 3.0 * M_PI, theta3_inv}));
      } else if (xacc::container::contains(SELF_ADJOINT_CLIFFORD_GATES,
                                           gate->name())) {
        // Handle Clifford gates:
        in_circuit->addInstruction(gate->clone());
      }
    }
  } 
@@ -125,11 +130,33 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
      return random_paulis;
    }(n);

    const auto new_paulis_as_layer = pauliListToLayer(new_paulis);
    const auto gateToLayerInfo = [](xacc::InstPtr gate, int nbQubits) {
      qcor::utils::CliffordGateLayer_t result;
      std::vector<int> operands;
      for (const auto &bit : gate->bits()) {
        operands.emplace_back(bit);
      }

      for (int i = 0; i < nbQubits; ++i) {
        if (!xacc::container::contains(operands, i)) {
          result.emplace_back(std::make_pair("I", std::vector<int>{i}));
        }
      }

      result.emplace_back(std::make_pair(gate->name(), operands));
      return result;
    };

    const auto current_net_paulis_as_layer = pauliListToLayer(net_paulis);
    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 new_paulis_as_layer = pauliListToLayer(new_paulis);
        const auto new_net_paulis_reps =
            qcor::utils::computeCircuitSymplecticRepresentations(
            {new_paulis_as_layer, current_net_paulis_as_layer}, n, srep_dict);
                {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);
@@ -142,10 +169,6 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
          xacc::info(ss.str());
        }

    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 auto [theta1, theta2, theta3] = decomposeU3Angle(gate);
        // Compute the pseudo_inverse gate:
@@ -155,6 +178,27 @@ createMirrorCircuit(std::shared_ptr<CompositeInstruction> in_circuit) {
                net_paulis[qubit]);
        mirrorCircuit.emplace_back(
            createU3GateFromAngle(qubit, theta1_new, theta2_new, theta3_new));
      } else if (xacc::container::contains(SELF_ADJOINT_CLIFFORD_GATES,
                                           gate->name())) {
        mirrorCircuit.emplace_back(gate->clone());
        // we need to account for how the net pauli changes when it gets passed
        // through the clifford layers
        const auto new_net_paulis_reps =
            qcor::utils::computeCircuitSymplecticRepresentations(
                {gateToLayerInfo(gate, n), current_net_paulis_as_layer,
                 gateToLayerInfo(gate, n)},
                n, srep_dict);

        // Update the tracking net
        net_paulis = qcor::utils::find_pauli_labels(new_net_paulis_reps.second);
        {
          std::stringstream ss;
          ss << "Net Pauli: ";
          for (const auto &p : net_paulis) {
            ss << p << " ";
          }
          xacc::info(ss.str());
        }
      }
    }
  }
+43 −0
Original line number Diff line number Diff line
@@ -90,6 +90,49 @@ TEST(MirrorCircuitTester, checkMultipleU3) {
  EXPECT_EQ(allBitStrings.size(), 4);
}

TEST(MirrorCircuitTester, checkCliffordGates) {
  auto provider = xacc::getIRProvider("quantum");
  constexpr int NUM_TESTS = 1000;
  auto accelerator = xacc::getAccelerator("qpp", {{"shots", 1024}});
  std::set<std::string> allBitStrings;
  for (int i = 0; i < NUM_TESTS; ++i) {
    auto circuit = provider->createComposite(std::string("test") + std::to_string(i));
    const double theta1 = random_angle();
    const double phi1 = random_angle();
    const double lambda1 = random_angle();
    circuit->addInstruction(provider->createInstruction(
        "U", {0},
        std::vector<xacc::InstructionParameter>{theta1, phi1, lambda1}));
    const double theta2 = random_angle();
    const double phi2 = random_angle();
    const double lambda2 = random_angle();
    circuit->addInstruction(provider->createInstruction(
        "U", {1},
        std::vector<xacc::InstructionParameter>{theta2, phi2, lambda2}));
    circuit->addInstruction(provider->createInstruction("CNOT", {0, 1}));
    circuit->addInstruction(provider->createInstruction("H", {0}));
    circuit->addInstruction(provider->createInstruction("H", {1}));
    auto [mirror_cir, expected_result] = qcor::createMirrorCircuit(
        std::make_shared<qcor::CompositeInstruction>(circuit));
    const std::string expectedBitString =
        std::to_string(expected_result[0]) + std::to_string(expected_result[1]);
    std::cout << "HOWDY: \n" << mirror_cir->toString() << "\n";
    std::cout << "Expected bitstring: " << expectedBitString << "\n";
    auto mirror_circuit = provider->createComposite("test_mirror");
    mirror_circuit->addInstructions(mirror_cir->getInstructions());
    mirror_circuit->addInstruction(provider->createInstruction("Measure", {0}));
    mirror_circuit->addInstruction(provider->createInstruction("Measure", {1}));
    auto mc_buffer = xacc::qalloc(2);
    accelerator->execute(mc_buffer, mirror_circuit);
    mc_buffer->print();
    EXPECT_EQ(mc_buffer->getMeasurementCounts().size(), 1);
    EXPECT_EQ(mc_buffer->getMeasurementCounts()[expectedBitString], 1024);
    allBitStrings.emplace(expectedBitString);
  }
  // Cover both cases (randomized Pauli worked)
  EXPECT_EQ(allBitStrings.size(), 4);
}

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