From 355ac14944382e9521bdb95a23764d5c16988654 Mon Sep 17 00:00:00 2001 From: Austin Adams Date: Tue, 8 Oct 2024 01:16:05 -0400 Subject: [PATCH] XaccQuantum: Fix broken ccx (Toffoli) The current handling of ccx has two problems in HEAD: 1. It is defined with two qubit operands instead of three 2. The implementation in XaccQuantum attempts to create an XACC instruction that does not exist Problem #1 manifests as this assertion tripped inside QIR-EE: $ build/bin/qir-xacc -i test/data/bell_ccx.ll --accelerator qpp terminate called after throwing an instance of 'qiree::RuntimeError' what(): /home/austin/Documents/school/gatech/grad/qsvt/qiree/src/qiree/detail/FunctionChecker.hh:116: qir-ee: runtime error: arg_size == irfunc_.arg_size() failed: incorrect QIR binding supplied: '__quantum__qis__ccx__body' has 3 arguments but QIR-EE expected 2 Aborted Problem #2 pops up when XACC throws the following exception: $ build/bin/qir-xacc -i test/data/bell_ccx.ll --accelerator qpp terminate called after throwing an instance of 'std::runtime_error' what(): Invalid instruction name - CCX Aborted This commit fixes both issues. --- src/qiree/Executor.cc | 6 ++- src/qiree/QuantumInterface.hh | 2 +- src/qiree/QuantumNotImpl.cc | 2 +- src/qiree/QuantumNotImpl.hh | 2 +- src/qirxacc/XaccQuantum.cc | 73 +++++++++++++++++++++++++++++++++-- src/qirxacc/XaccQuantum.hh | 25 +++++++++++- test/data/bell_ccx.ll | 43 +++++++++++++++++++++ test/qiree/Executor.test.cc | 21 ++++++++++ test/qiree/QuantumTestImpl.cc | 9 +++-- test/qiree/QuantumTestImpl.hh | 2 +- 10 files changed, 170 insertions(+), 15 deletions(-) create mode 100644 test/data/bell_ccx.ll diff --git a/src/qiree/Executor.cc b/src/qiree/Executor.cc index 5640549..702a08d 100644 --- a/src/qiree/Executor.cc +++ b/src/qiree/Executor.cc @@ -77,9 +77,11 @@ bool QIREE_QIS_FUNCTION(read_result, body)(std::uintptr_t arg1) //---------------------------------------------------------------------------// // GATES //---------------------------------------------------------------------------// -void QIREE_QIS_FUNCTION(ccx, body)(std::uintptr_t arg1, std::uintptr_t arg2) +void QIREE_QIS_FUNCTION(ccx, body)(std::uintptr_t arg1, + std::uintptr_t arg2, + std::uintptr_t arg3) { - return q_interface_->ccx(Qubit{arg1}, Qubit{arg2}); + return q_interface_->ccx(Qubit{arg1}, Qubit{arg2}, Qubit{arg3}); } void QIREE_QIS_FUNCTION(cnot, body)(std::uintptr_t arg1, std::uintptr_t arg2) { diff --git a/src/qiree/QuantumInterface.hh b/src/qiree/QuantumInterface.hh index 5ba603a..ae2e69b 100644 --- a/src/qiree/QuantumInterface.hh +++ b/src/qiree/QuantumInterface.hh @@ -67,7 +67,7 @@ class QuantumInterface //@{ //! \name Gates - virtual void ccx(Qubit, Qubit) = 0; //!< body + virtual void ccx(Qubit, Qubit, Qubit) = 0; //!< body virtual void cnot(Qubit, Qubit) = 0; //!< body virtual void cx(Qubit, Qubit) = 0; //!< body virtual void cy(Qubit, Qubit) = 0; //!< body diff --git a/src/qiree/QuantumNotImpl.cc b/src/qiree/QuantumNotImpl.cc index 7c00ab8..7b28840 100644 --- a/src/qiree/QuantumNotImpl.cc +++ b/src/qiree/QuantumNotImpl.cc @@ -32,7 +32,7 @@ QState QuantumNotImpl::read_result(Result) { QIREE_NOT_IMPLEMENTED("quantum instruction 'read_result.body'"); } -void QuantumNotImpl::ccx(Qubit, Qubit) +void QuantumNotImpl::ccx(Qubit, Qubit, Qubit) { QIREE_NOT_IMPLEMENTED("quantum instruction 'ccx.body'"); } diff --git a/src/qiree/QuantumNotImpl.hh b/src/qiree/QuantumNotImpl.hh index 86ce5de..b6a93cd 100644 --- a/src/qiree/QuantumNotImpl.hh +++ b/src/qiree/QuantumNotImpl.hh @@ -31,7 +31,7 @@ class QuantumNotImpl : virtual public QuantumInterface //@{ //! \name Gates - void ccx(Qubit, Qubit) override; + void ccx(Qubit, Qubit, Qubit) override; void cnot(Qubit, Qubit) override; void cx(Qubit, Qubit) override; void cy(Qubit, Qubit) override; diff --git a/src/qirxacc/XaccQuantum.cc b/src/qirxacc/XaccQuantum.cc index 7c3df24..336ba9a 100644 --- a/src/qirxacc/XaccQuantum.cc +++ b/src/qirxacc/XaccQuantum.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include "qiree/Assert.hh" @@ -152,13 +153,15 @@ QState XaccQuantum::read_result(Result r) //---------------------------------------------------------------------------// // QUANTUM INSTRUCTION MAPPING //---------------------------------------------------------------------------// -void XaccQuantum::ccx(Qubit q1, Qubit q2) +void XaccQuantum::ccx(Qubit q1, Qubit q2, Qubit q3) { - this->add_instruction("CCX", {q1, q2}); + // XACC IR does not have a Toffoli gate + this->add_ctrl_list_instruction("X", {q1, q2}, q3); } void XaccQuantum::ccnot(Qubit q1, Qubit q2, Qubit q3) { - this->add_instruction("CCNOT", {q1, q2, q3}); + // XACC IR does not have a Toffoli gate + this->add_ctrl_list_instruction("X", {q1, q2}, q3); } void XaccQuantum::cnot(Qubit q1, Qubit q2) { @@ -324,6 +327,22 @@ template void XaccQuantum::add_instruction(std::string s, std::initializer_list qs, Ts... args) +{ + this->add_instruction_to( + cur_circuit_, std::move(s), qs, std::forward(args)...); +} + +//---------------------------------------------------------------------------// +/*! + * Add an instruction with multiple qubits to a particular XACC + * CompositeInstruction. + */ +template +void XaccQuantum::add_instruction_to( + std::shared_ptr circuit, + std::string s, + std::initializer_list qs, + Ts... args) { // Transform opaque qubit types into raw integer indices std::vector q_indices(qs.size()); @@ -338,7 +357,53 @@ void XaccQuantum::add_instruction(std::string s, std::move(s), q_indices, VecInstr{std::forward(args)...}); // Add to current quantum circuit - cur_circuit_->addInstruction(std::move(instr)); + circuit->addInstruction(std::move(instr)); +} + +//---------------------------------------------------------------------------// +/*! + * Add an instruction with the control indices provided. + */ +template +void XaccQuantum::add_ctrl_indices_instruction(std::string s, + std::vector ctrl_indices, + Qubit q, + Ts... args) +{ + std::shared_ptr tmp + = provider_->createComposite("tmp"); + this->add_instruction_to(tmp, std::move(s), {q}, std::forward(args)...); + + std::shared_ptr cu + = std::static_pointer_cast( + xacc::getService("C-U")); + cu->expand({{"U", tmp}, {"control-idx", ctrl_indices}}); + + for (int i = 0; i < cu->nInstructions(); i++) + { + cur_circuit_->addInstruction(cu->getInstruction(i)); + } +} + +//---------------------------------------------------------------------------// +/*! + * Add an instruction with the control indices provided as a list of QIR + * pointers (for convenience). + */ +template +void XaccQuantum::add_ctrl_list_instruction( + std::string s, std::initializer_list ctrl_list, Qubit q, Ts... args) +{ + std::vector ctrl_indices(ctrl_list.size()); + std::transform(ctrl_list.begin(), + ctrl_list.end(), + ctrl_indices.begin(), + [this](Qubit q) { + QIREE_EXPECT(q.value < this->num_qubits()); + return static_cast(q.value); + }); + add_ctrl_indices_instruction( + std::move(s), ctrl_indices, q, std::forward(args)...); } //---------------------------------------------------------------------------// diff --git a/src/qirxacc/XaccQuantum.hh b/src/qirxacc/XaccQuantum.hh index 1fb793f..5a0c77c 100644 --- a/src/qirxacc/XaccQuantum.hh +++ b/src/qirxacc/XaccQuantum.hh @@ -75,7 +75,7 @@ class XaccQuantum final : virtual public QuantumNotImpl //!@{ //! \name Circuit construction - void ccx(Qubit, Qubit) final; + void ccx(Qubit, Qubit, Qubit) final; void ccnot(Qubit, Qubit, Qubit); // TODO: not in examples or qir runner void cnot(Qubit, Qubit) final; void cx(Qubit, Qubit) final; @@ -145,6 +145,29 @@ class XaccQuantum final : virtual public QuantumNotImpl template void add_instruction(std::string s, std::initializer_list qs, Ts... args); + + // Add an instruction with multiple qubits to a particular XACC + // CompositeInstruction + template + void add_instruction_to(std::shared_ptr circuit, + std::string s, + std::initializer_list qs, + Ts... args); + + // Add an instruction with the control indices provided + template + void add_ctrl_indices_instruction(std::string s, + std::vector ctrl_indices, + Qubit q, + Ts... args); + + // Add an instruction with the control indices provided as a list of QIR + // pointers (for convenience) + template + void add_ctrl_list_instruction(std::string s, + std::initializer_list ctrl_list, + Qubit q, + Ts... args); }; //---------------------------------------------------------------------------// diff --git a/test/data/bell_ccx.ll b/test/data/bell_ccx.ll new file mode 100644 index 0000000..e5b2ea7 --- /dev/null +++ b/test/data/bell_ccx.ll @@ -0,0 +1,43 @@ +; ModuleID = 'Bell_ccx' +source_filename = "Bell_ccx" + +%Qubit = type opaque +%Result = type opaque + +define void @main() #0 { +entry: + call void @__quantum__qis__h__body(%Qubit* null) + call void @__quantum__qis__x__body(%Qubit* inttoptr (i64 1 to %Qubit*)) + call void @__quantum__qis__ccx__body(%Qubit* null, %Qubit* inttoptr (i64 1 to %Qubit*), %Qubit* inttoptr (i64 2 to %Qubit*)) + call void @__quantum__qis__mz__body(%Qubit* null, %Result* null) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) + call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 2 to %Result*)) + call void @__quantum__rt__array_record_output(i64 3, i8* null) + call void @__quantum__rt__result_record_output(%Result* null, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) + ret void +} + +declare void @__quantum__qis__h__body(%Qubit*) + +declare void @__quantum__qis__x__body(%Qubit*) + +declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) + +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 + +declare void @__quantum__rt__array_record_output(i64, i8*) + +declare void @__quantum__rt__result_record_output(%Result*, i8*) + +attributes #0 = { "entry_point" "num_required_qubits"="3" "num_required_results"="3" "output_labeling_schema" "qir_profiles"="custom" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} + diff --git a/test/qiree/Executor.test.cc b/test/qiree/Executor.test.cc index 64379cb..3101736 100644 --- a/test/qiree/Executor.test.cc +++ b/test/qiree/Executor.test.cc @@ -87,6 +87,27 @@ tear_down result.commands.str()); } +//---------------------------------------------------------------------------// +TEST_F(ExecutorTest, bell_ccx) +{ + auto result = this->run("bell_ccx.ll"); + EXPECT_EQ(R"( +set_up(q=3, r=3) +h(Q{0}) +TODO: x.body +ccx(Q{0}, Q{1}, Q{2}) +mz(Q{0},R{0}) +mz(Q{1},R{1}) +mz(Q{2},R{2}) +array_record_output(3) +result_record_output(R{0}) +result_record_output(R{1}) +result_record_output(R{2}) +tear_down +)", + result.commands.str()); +} + //---------------------------------------------------------------------------// TEST_F(ExecutorTest, rotation) { diff --git a/test/qiree/QuantumTestImpl.cc b/test/qiree/QuantumTestImpl.cc index 1e1299d..a02a222 100644 --- a/test/qiree/QuantumTestImpl.cc +++ b/test/qiree/QuantumTestImpl.cc @@ -86,6 +86,11 @@ void QuantumTestImpl::cnot(Qubit q1, Qubit q2) tr_->commands << "cnot(" << q1 << ", " << q2 << ")\n"; } +void QuantumTestImpl::ccx(Qubit q1, Qubit q2, Qubit q3) +{ + tr_->commands << "ccx(" << q1 << ", " << q2 << ", " << q3 << ")\n"; +} + //---------------------------------------------------------------------------// Result QuantumTestImpl::m(Qubit) { @@ -102,10 +107,6 @@ Result QuantumTestImpl::mresetz(Qubit) tr_->commands << "TODO: mresetz.body\n"; return {}; } -void QuantumTestImpl::ccx(Qubit, Qubit) -{ - tr_->commands << "TODO: ccx.body\n"; -} void QuantumTestImpl::cx(Qubit, Qubit) { tr_->commands << "TODO: cx.body\n"; diff --git a/test/qiree/QuantumTestImpl.hh b/test/qiree/QuantumTestImpl.hh index 8a917da..f89732f 100644 --- a/test/qiree/QuantumTestImpl.hh +++ b/test/qiree/QuantumTestImpl.hh @@ -60,7 +60,7 @@ class QuantumTestImpl final : public QuantumInterface Result measure(Array, Array) override; Result mresetz(Qubit) override; - void ccx(Qubit, Qubit) override; + void ccx(Qubit, Qubit, Qubit) override; void cx(Qubit, Qubit) override; void cy(Qubit, Qubit) override; void cz(Qubit, Qubit) override;