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: Nguyen, Thien Minh's avatarThien Nguyen <nguyentm@ornl.gov>
parent 78922f6d
{
"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
// 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
......@@ -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,
......@@ -173,7 +199,7 @@ std::pair<bool, xacc::HeterogeneousMap> MirrorCircuitValidator::validate(
if (options.keyExists<double>("epsilon")) {
eps = options.get<double>("epsilon");
}
qpu->updateConfiguration({{"shots", n_shots}});
std::vector<double> trial_success_probs;
auto provider = xacc::getIRProvider("quantum");
......@@ -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");
......
......@@ -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
}
};
......
......@@ -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();
......
......@@ -24,6 +24,7 @@ std::vector<int> __qubit_map = {};
std::string __qrt_env = "nisq";
bool __print_final_submission = false;
std::string __print_final_submission_filename = "";
bool __validate_nisq_execution = false;
void execute_pass_manager(
std::shared_ptr<qcor::CompositeInstruction> optional_composite) {
......
......@@ -250,7 +250,8 @@ extern std::string __qrt_env;
// Print final CompositeInstruction for backend submission
extern bool __print_final_submission;
extern std::string __print_final_submission_filename;
// Backend execution validation
extern bool __validate_nisq_execution;
// Execute the pass manager on the provided kernel.
// If none provided, execute the pass manager on the current QRT kernel.
void execute_pass_manager(
......
......@@ -348,6 +348,11 @@ def main(argv=None):
parser.add_argument('-qs-runtime', metavar='', help='Specifies the classical capabilites of the QIR runtime. Valid options: BasicQuantumFunctionality, BasicMeasurementFeedback, FullComputation')
parser.add_argument('-qdk-version', metavar='', help='Specifies the Microsoft Quantum SDK version to use.')
# Flag to enable validation:
# Currently, it means enabling Pauli-randomized mirror circuit benchmarking:
# i.e., executing a set of benchmarking circuits related to the original circuit that we know the expected bitstring
parser.add_argument('-validate', metavar='', help='Enables backend execution validation via the mirror circuit technique.')
args = parser.parse_args(sys.argv)
if '-pythonpath' in sys.argv[1:]:
......@@ -608,6 +613,13 @@ def main(argv=None):
if qrtName == 'ftqc':
sys.argv += ['-D_QCOR_FTQC_RUNTIME']
if '-validate' in sys.argv[1:]:
sys.argv.remove('-validate')
sys.argv += ['-D__internal__qcor__validate__execution']
if '-D_QCOR_FTQC_RUNTIME' in sys.argv:
print('Execution validation cannot be used with FTQC runtime.')
exit(1)
# if not empty
if '@QCOR_APPEND_PLUGIN_PATH@':
sys.argv += ['-D__internal__qcor__compile__plugin__path=\"@QCOR_APPEND_PLUGIN_PATH@\"']
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment