Commit 68b175e4 authored by Nguyen, Thien Minh's avatar Nguyen, Thien Minh
Browse files

Wrapping up mirror circuit impl



As the first prototype, I only expose a single flag to enable validation if NISQ kernel execution.

If enabled, kernel execution (NISQ) will be accompanied by Pauli-randomized mirror circuits to determine if the backend can produce the expected readout.

Signed-off-by: default avatarThien Nguyen <nguyentm@ornl.gov>
parent 78922f6d
Loading
Loading
Loading
Loading
+350 −0
Original line number Diff line number Diff line
{
  "errors": [
    {
      "type": "qerror",
      "operations": [
        "u1"
      ],
      "instructions": [
        [
          {
            "name": "x",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "id",
            "qubits": [
              0
            ]
          }
        ]
      ],
      "probabilities": [
        0.00025,
        0.00025,
        0.00025,
        0.99925
      ]
    },
    {
      "type": "qerror",
      "operations": [
        "u2"
      ],
      "instructions": [
        [
          {
            "name": "x",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "id",
            "qubits": [
              0
            ]
          }
        ]
      ],
      "probabilities": [
        0.00025,
        0.00025,
        0.00025,
        0.99925
      ]
    },
    {
      "type": "qerror",
      "operations": [
        "u3"
      ],
      "instructions": [
        [
          {
            "name": "x",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "id",
            "qubits": [
              0
            ]
          }
        ]
      ],
      "probabilities": [
        0.00025,
        0.00025,
        0.00025,
        0.99925
      ]
    },
    {
      "type": "qerror",
      "operations": [
        "cx"
      ],
      "instructions": [
        [
          {
            "name": "x",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              0
            ]
          }
        ],
        [
          {
            "name": "x",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "x",
            "qubits": [
              0
            ]
          },
          {
            "name": "x",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              0
            ]
          },
          {
            "name": "x",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              0
            ]
          },
          {
            "name": "x",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "x",
            "qubits": [
              0
            ]
          },
          {
            "name": "y",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              0
            ]
          },
          {
            "name": "y",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              0
            ]
          },
          {
            "name": "y",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "x",
            "qubits": [
              0
            ]
          },
          {
            "name": "z",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "y",
            "qubits": [
              0
            ]
          },
          {
            "name": "z",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "z",
            "qubits": [
              0
            ]
          },
          {
            "name": "z",
            "qubits": [
              1
            ]
          }
        ],
        [
          {
            "name": "id",
            "qubits": [
              0
            ]
          }
        ]
      ],
      "probabilities": [
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.0006250000000000001,
        0.9906250000000001
      ]
    }
  ]
}
 No newline at end of file
+24 −0
Original line number Diff line number Diff line
// Compile to run with validation mode: 
// Qpp (noiseless)
// qcor -validate validate_execution.cpp -shots 1024
// Aer (noisy)
// qcor -validate validate_execution.cpp -shots 1024 -qpu aer[noise-model:noise_model.json]
__qpu__ void noisy_zero(qreg q, int cx_count) {
  H(q);
  for (int i = 0; i < cx_count; i++) {
    X::ctrl(q[0], q[1]);
  }
  H(q);
  Measure(q);
}

int main() {
  // qcor::set_verbose(true);
  // On the noisy simulator, the validation will be successful for 1 cycle
  // but will probably fail when running with 10 cycles.
  const int nb_cycles = 1;
  // const int nb_cycles = 10;
  auto q = qalloc(2);
  noisy_zero(q, nb_cycles);
  q.print();
}
 No newline at end of file
+50 −23
Original line number Diff line number Diff line
@@ -4,10 +4,27 @@
#include "qcor_ir.hpp"
#include "qcor_pimpl_impl.hpp"
#include "xacc.hpp"
#include "xacc_plugin.hpp"
#include "xacc_service.hpp"
#include <cassert>
#include <random>
#include "xacc_plugin.hpp"
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() - 1; 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 xacc {
namespace quantum {
// Helper to convert a gate
@@ -76,7 +93,7 @@ public:
  }

  void visit(Measure &measure) override {
    xacc::error("The mirror circuit must not contain measure gates.");
    // Ignore measure
  }

  void visit(Identity &i) override {}
@@ -129,33 +146,42 @@ public:
    visit(ry2);
  }

  std::shared_ptr<CompositeInstruction> getProgram() { return m_program; }
  std::shared_ptr<CompositeInstruction> getProgram() { return balanceLayer(); }

private:
  std::shared_ptr<CompositeInstruction> balanceLayer() {
    // Balance all layers
    // The mirroring protocol doesn't work well when there are layers that has
    // no gate on a qubit line, hence, just add an Identity there:
    auto program = m_gateRegistry->createComposite("temp_composite");
    const auto d = m_program->depth();
    const auto nbQubits = m_program->nPhysicalBits();
    for (int layer = 0; layer < d; ++layer) {
      std::set<int> qubits;
      auto current_layers = getLayer(m_program, layer);
      for (const auto &gate : current_layers) {
        program->addInstruction(gate);
        for (const auto &bit : gate->bits()) {
          qubits.emplace(bit);
        }
      }
      if (qubits.size() < nbQubits) {
        for (int i = 0; i < nbQubits; ++i) {
          if (!xacc::container::contains(qubits, i)) {
            program->addInstruction(
                m_gateRegistry->createInstruction("U", {(size_t)i}, {0.0, 0.0, 0.0}));
          }
        }
      }
    }
    return program;
  }
  std::shared_ptr<CompositeInstruction> m_program;
  std::shared_ptr<xacc::IRProvider> m_gateRegistry;
};
} // namespace quantum
} // namespace xacc

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() - 1; 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<bool, xacc::HeterogeneousMap> MirrorCircuitValidator::validate(
    std::shared_ptr<xacc::Accelerator> qpu,
@@ -219,7 +245,8 @@ std::pair<bool, xacc::HeterogeneousMap> MirrorCircuitValidator::validate(
}

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

+3 −0
Original line number Diff line number Diff line
@@ -51,6 +51,9 @@ public:
#endif
#ifdef __internal__qcor__print__final__submission
    xacc::internal_compiler::__print_final_submission = true;
#endif
#ifdef __internal__qcor__validate__execution
    xacc::internal_compiler::__validate_nisq_execution = true;
#endif
  }
};
+18 −0
Original line number Diff line number Diff line
@@ -406,6 +406,24 @@ class NISQ : public ::quantum::QuantumRuntime,
      xacc::storeBuffer(xacc::as_shared_ptr(buffer));
      xacc::internal_compiler::execute(
          buffer, program->as_xacc());
      if (__validate_nisq_execution) {
        auto [validated, validationData] = validate_backend_execution(program);
        if (!validated) {
          std::cout << "Failed to validate the execution of the program on the "
                    << get_qpu()->name() << " backend.\n";
          // NOTES: currently, we validate the whole program (all layers),
          // we can run layer-by-layer to determine at which depth the program
          // starts to fail.
          std::cout << "Result buffer:\n";
          buffer->print();
          throw std::logic_error(
              "Failed to validate backend execution in validation mode.\n");
        }
        else {
          std::cout << "Successfully validate the execution of the program on the "
                    << get_qpu()->name() << " backend.\n";
        }
      }
    }

    clearProgram();
Loading