Loading mlir/dialect/include/Quantum/QuantumOps.td +19 −3 Original line number Diff line number Diff line Loading @@ -4,7 +4,6 @@ include "QuantumDialect.td" include "mlir/Interfaces/SideEffectInterfaces.td" def QubitType : OpaqueType<"quantum", "Qubit", "opaque qubit type">; def ResultType : OpaqueType<"quantum", "Result", "opaque result type">; def ArrayType : OpaqueType<"quantum", "Array", "opaque array type">; Loading Loading @@ -228,10 +227,27 @@ def TupleUnpackOp : QuantumOp<"tupleUnpack", []> { } def CreateCallableOp : QuantumOp<"createCallable", []> { let arguments = (ins FlatSymbolRefAttr:$functors); let arguments = (ins FlatSymbolRefAttr:$functors, Variadic<AnyType>:$captures); let results = (outs CallableType:$callable); let printer = [{ auto op = *this; p << "q.createCallable" << "(" << op.functors() << ") : " << op.callable().getType(); }]; p << "q.createCallable" << "(" << op.functors() << ") "; if (!op.captures().empty()) { p << "capture " << op.captures() << "(" << op.captures().getType() << ")"; } }]; } def ConditionalOp : QuantumOp<"ifOp", []> { let summary = "if-then-else operation conditioned on a quantum Measure"; // Must be conditioned on a Result type (only then clause for now...) let arguments = (ins ResultType:$result_bit, CallableType:$then_callable); let results = (outs); let printer = [{ auto op = *this; p << "q.If " << op.result_bit() << " { invoke " << op.then_callable() << " }"; }]; } #endif // Quantum_OPS No newline at end of file mlir/parsers/qasm3/examples/measure_conditional.qasm 0 → 100644 +80 −0 Original line number Diff line number Diff line OPENQASM 3; include "qelib1.inc"; // NISQ-mode lowering to conditional (If) statements // Compile targeting nisq runtime and a capable QPU: // (1) Qpp simulator (default): // qcor -qrt nisq -shots 1024 measure_conditional.qasm -print-final-submission // (2) Aer simulator (inspect the QObj to see the use of "conditional" to control quantum instructions) // qcor -qrt nisq -qpu aer -shots 1024 measure_conditional.qasm -print-final-submission // (3) Honeywell: see the OpenQASM2 with if statements. // qcor -qrt nisq -qpu honeywell:HQS-LT-S1-APIVAL -shots 1024 measure_conditional.qasm -print-final-submission // Expected to get 4 bits (iteratively) of 1011 (or 1101 LSB) = 11(decimal): // phi_est = 11/16 (denom = 16 since we have 4 bits) // => phi = 2pi * 11/16 = 11pi/8 = 2pi - 5pi/8 // i.e. we estimate the -5*pi/8 angle... qubit q[2]; const bits_precision = 4; bit c[bits_precision]; // Prepare the eigen-state: |1> x q[1]; // First bit h q[0]; // Controlled rotation: CU^k for i in [0:8] { cphase(-5*pi/8) q[0], q[1]; } h q[0]; // Measure and reset measure q[0] -> c[0]; reset q[0]; // Second bit h q[0]; for i in [0:4] { cphase(-5*pi/8) q[0], q[1]; } // Conditional rotation if (c[0] == 1) { rz(-pi/2) q[0]; } h q[0]; // Measure and reset measure q[0] -> c[1]; reset q[0]; // Third bit h q[0]; for i in [0:2] { cphase(-5*pi/8) q[0], q[1]; } // Conditional rotation if (c[0] == 1) { rz(-pi/4) q[0]; } if (c[1] == 1) { rz(-pi/2) q[0]; } h q[0]; // Measure and reset measure q[0] -> c[2]; reset q[0]; // Fourth bit h q[0]; cphase(-5*pi/8) q[0], q[1]; // Conditional rotation if (c[0] == 1) { rz(-pi/8) q[0]; } if (c[1] == 1) { rz(-pi/4) q[0]; } if (c[2] == 1) { rz(-pi/2) q[0]; } h q[0]; measure q[0] -> c[3]; No newline at end of file mlir/parsers/qasm3/openqasmv3_mlir_generator.cpp +24 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,28 @@ void OpenQasmV3MLIRGenerator::initialize_mlirgen( file_name = function; add_entry_point = _add_entry_point; // Only enable the rewrite to NISQ If-statements when the compilation // targets NISQ qrt for some specific QPUs: static const std::vector<std::string> IF_STMT_CAPABLE_QPUS{"qpp", "aer", "honeywell"}; if (extra_quantum_args.find("qrt") != extra_quantum_args.end() && extra_quantum_args["qrt"] == "nisq") { // Default is qpp (i.e., not provided) if (extra_quantum_args.find("qpu") == extra_quantum_args.end()) { enable_qir_apply_ifelse = true; } else { for (const auto &name_to_check : IF_STMT_CAPABLE_QPUS) { const auto qpu_name = extra_quantum_args["qpu"]; if (qpu_name.rfind(name_to_check, 0) == 0) { // QPU start with aer, honeywell, etc. // (it could have backend name customization after ':') enable_qir_apply_ifelse = true; break; } } } } // Useful opaque type defs llvm::StringRef qubit_type_name("Qubit"), array_type_name("Array"), result_type_name("Result"); Loading Loading @@ -146,7 +168,8 @@ void OpenQasmV3MLIRGenerator::mlirgen(const std::string &src) { using namespace qasm3; if (!visitor) { visitor = std::make_shared<qasm3_visitor>(builder, m_module, file_name); visitor = std::make_shared<qasm3_visitor>(builder, m_module, file_name, enable_qir_apply_ifelse); } ANTLRInputStream input(src); Loading mlir/parsers/qasm3/openqasmv3_mlir_generator.hpp +3 −1 Original line number Diff line number Diff line Loading @@ -11,7 +11,9 @@ class OpenQasmV3MLIRGenerator : public qcor::QuantumMLIRGenerator { std::string file_name = "main"; bool add_entry_point = true; bool add_custom_return = false; // Enable special code-gen mode for specific targets that // support NISQ-like conditional statements. bool enable_qir_apply_ifelse = false; mlir::Type return_type; mlir::Type qubit_type; Loading mlir/parsers/qasm3/qasm3_visitor.hpp +5 −3 Original line number Diff line number Diff line Loading @@ -30,8 +30,10 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor { ScopedSymbolTable* getScopedSymbolTable() { return &symbol_table; } // The constructor, instantiates commonly used opaque types qasm3_visitor(mlir::OpBuilder b, mlir::ModuleOp m, std::string& fname) : builder(b), file_name(fname), m_module(m) { qasm3_visitor(mlir::OpBuilder b, mlir::ModuleOp m, std::string &fname, bool enable_nisq_conditional = false) : builder(b), file_name(fname), m_module(m), enable_nisq_ifelse(enable_nisq_conditional) { auto context = b.getContext(); llvm::StringRef qubit_type_name("Qubit"), array_type_name("Array"), result_type_name("Result"); Loading Loading @@ -189,7 +191,7 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor { mlir::OpBuilder builder; mlir::ModuleOp m_module; std::string file_name = ""; bool enable_nisq_ifelse = false; // We keep reference to these blocks so that // we can handle break/continue correctly mlir::Block* current_loop_exit_block; Loading Loading
mlir/dialect/include/Quantum/QuantumOps.td +19 −3 Original line number Diff line number Diff line Loading @@ -4,7 +4,6 @@ include "QuantumDialect.td" include "mlir/Interfaces/SideEffectInterfaces.td" def QubitType : OpaqueType<"quantum", "Qubit", "opaque qubit type">; def ResultType : OpaqueType<"quantum", "Result", "opaque result type">; def ArrayType : OpaqueType<"quantum", "Array", "opaque array type">; Loading Loading @@ -228,10 +227,27 @@ def TupleUnpackOp : QuantumOp<"tupleUnpack", []> { } def CreateCallableOp : QuantumOp<"createCallable", []> { let arguments = (ins FlatSymbolRefAttr:$functors); let arguments = (ins FlatSymbolRefAttr:$functors, Variadic<AnyType>:$captures); let results = (outs CallableType:$callable); let printer = [{ auto op = *this; p << "q.createCallable" << "(" << op.functors() << ") : " << op.callable().getType(); }]; p << "q.createCallable" << "(" << op.functors() << ") "; if (!op.captures().empty()) { p << "capture " << op.captures() << "(" << op.captures().getType() << ")"; } }]; } def ConditionalOp : QuantumOp<"ifOp", []> { let summary = "if-then-else operation conditioned on a quantum Measure"; // Must be conditioned on a Result type (only then clause for now...) let arguments = (ins ResultType:$result_bit, CallableType:$then_callable); let results = (outs); let printer = [{ auto op = *this; p << "q.If " << op.result_bit() << " { invoke " << op.then_callable() << " }"; }]; } #endif // Quantum_OPS No newline at end of file
mlir/parsers/qasm3/examples/measure_conditional.qasm 0 → 100644 +80 −0 Original line number Diff line number Diff line OPENQASM 3; include "qelib1.inc"; // NISQ-mode lowering to conditional (If) statements // Compile targeting nisq runtime and a capable QPU: // (1) Qpp simulator (default): // qcor -qrt nisq -shots 1024 measure_conditional.qasm -print-final-submission // (2) Aer simulator (inspect the QObj to see the use of "conditional" to control quantum instructions) // qcor -qrt nisq -qpu aer -shots 1024 measure_conditional.qasm -print-final-submission // (3) Honeywell: see the OpenQASM2 with if statements. // qcor -qrt nisq -qpu honeywell:HQS-LT-S1-APIVAL -shots 1024 measure_conditional.qasm -print-final-submission // Expected to get 4 bits (iteratively) of 1011 (or 1101 LSB) = 11(decimal): // phi_est = 11/16 (denom = 16 since we have 4 bits) // => phi = 2pi * 11/16 = 11pi/8 = 2pi - 5pi/8 // i.e. we estimate the -5*pi/8 angle... qubit q[2]; const bits_precision = 4; bit c[bits_precision]; // Prepare the eigen-state: |1> x q[1]; // First bit h q[0]; // Controlled rotation: CU^k for i in [0:8] { cphase(-5*pi/8) q[0], q[1]; } h q[0]; // Measure and reset measure q[0] -> c[0]; reset q[0]; // Second bit h q[0]; for i in [0:4] { cphase(-5*pi/8) q[0], q[1]; } // Conditional rotation if (c[0] == 1) { rz(-pi/2) q[0]; } h q[0]; // Measure and reset measure q[0] -> c[1]; reset q[0]; // Third bit h q[0]; for i in [0:2] { cphase(-5*pi/8) q[0], q[1]; } // Conditional rotation if (c[0] == 1) { rz(-pi/4) q[0]; } if (c[1] == 1) { rz(-pi/2) q[0]; } h q[0]; // Measure and reset measure q[0] -> c[2]; reset q[0]; // Fourth bit h q[0]; cphase(-5*pi/8) q[0], q[1]; // Conditional rotation if (c[0] == 1) { rz(-pi/8) q[0]; } if (c[1] == 1) { rz(-pi/4) q[0]; } if (c[2] == 1) { rz(-pi/2) q[0]; } h q[0]; measure q[0] -> c[3]; No newline at end of file
mlir/parsers/qasm3/openqasmv3_mlir_generator.cpp +24 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,28 @@ void OpenQasmV3MLIRGenerator::initialize_mlirgen( file_name = function; add_entry_point = _add_entry_point; // Only enable the rewrite to NISQ If-statements when the compilation // targets NISQ qrt for some specific QPUs: static const std::vector<std::string> IF_STMT_CAPABLE_QPUS{"qpp", "aer", "honeywell"}; if (extra_quantum_args.find("qrt") != extra_quantum_args.end() && extra_quantum_args["qrt"] == "nisq") { // Default is qpp (i.e., not provided) if (extra_quantum_args.find("qpu") == extra_quantum_args.end()) { enable_qir_apply_ifelse = true; } else { for (const auto &name_to_check : IF_STMT_CAPABLE_QPUS) { const auto qpu_name = extra_quantum_args["qpu"]; if (qpu_name.rfind(name_to_check, 0) == 0) { // QPU start with aer, honeywell, etc. // (it could have backend name customization after ':') enable_qir_apply_ifelse = true; break; } } } } // Useful opaque type defs llvm::StringRef qubit_type_name("Qubit"), array_type_name("Array"), result_type_name("Result"); Loading Loading @@ -146,7 +168,8 @@ void OpenQasmV3MLIRGenerator::mlirgen(const std::string &src) { using namespace qasm3; if (!visitor) { visitor = std::make_shared<qasm3_visitor>(builder, m_module, file_name); visitor = std::make_shared<qasm3_visitor>(builder, m_module, file_name, enable_qir_apply_ifelse); } ANTLRInputStream input(src); Loading
mlir/parsers/qasm3/openqasmv3_mlir_generator.hpp +3 −1 Original line number Diff line number Diff line Loading @@ -11,7 +11,9 @@ class OpenQasmV3MLIRGenerator : public qcor::QuantumMLIRGenerator { std::string file_name = "main"; bool add_entry_point = true; bool add_custom_return = false; // Enable special code-gen mode for specific targets that // support NISQ-like conditional statements. bool enable_qir_apply_ifelse = false; mlir::Type return_type; mlir::Type qubit_type; Loading
mlir/parsers/qasm3/qasm3_visitor.hpp +5 −3 Original line number Diff line number Diff line Loading @@ -30,8 +30,10 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor { ScopedSymbolTable* getScopedSymbolTable() { return &symbol_table; } // The constructor, instantiates commonly used opaque types qasm3_visitor(mlir::OpBuilder b, mlir::ModuleOp m, std::string& fname) : builder(b), file_name(fname), m_module(m) { qasm3_visitor(mlir::OpBuilder b, mlir::ModuleOp m, std::string &fname, bool enable_nisq_conditional = false) : builder(b), file_name(fname), m_module(m), enable_nisq_ifelse(enable_nisq_conditional) { auto context = b.getContext(); llvm::StringRef qubit_type_name("Qubit"), array_type_name("Array"), result_type_name("Result"); Loading Loading @@ -189,7 +191,7 @@ class qasm3_visitor : public qasm3::qasm3BaseVisitor { mlir::OpBuilder builder; mlir::ModuleOp m_module; std::string file_name = ""; bool enable_nisq_ifelse = false; // We keep reference to these blocks so that // we can handle break/continue correctly mlir::Block* current_loop_exit_block; Loading