Loading python/tests/CMakeLists.txt +7 −0 Original line number Diff line number Diff line Loading @@ -74,3 +74,10 @@ add_test (NAME qcor_python_jit_kernel_signature set_tests_properties(qcor_python_jit_kernel_signature PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_INSTALL_PREFIX}:$ENV{PYTHONPATH}") add_test (NAME qcor_python_compute_action COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_compute_action.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) set_tests_properties(qcor_python_compute_action PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_INSTALL_PREFIX}:$ENV{PYTHONPATH}") No newline at end of file python/tests/test_compute_action.py 0 → 100644 +129 −0 Original line number Diff line number Diff line import faulthandler faulthandler.enable() import unittest from qcor import * class TestKernelJIT(unittest.TestCase): def test_simple(self): @qjit def x_gate_standalone(q: qubit): X(q) @qjit def test_compute_action1(q : qreg, x : float): with compute: # control of x gate X.ctrl(q[0], q[1]) for i in range(3): H(q[i+1]) with action: Rz(q[3], x) @qjit def test_compute_action2(q : qreg, x : float): with compute: # control of x gate wrapped in a kernel x_gate_standalone.ctrl(q[0], q[1]) for i in range(3): H(q[i+1]) with action: Rz(q[3], x) @qjit def test_compute_action3(q : qreg, x : float): # Control of a kernel compute-action (version 1: ctrl of simple gate) test_compute_action1.ctrl(q[0], q[1:q.size()], x) @qjit def test_compute_action4(q : qreg, x : float): # Control of a kernel compute-action (version 2: ctrl of a kernel) test_compute_action2.ctrl(q[0], q[1:q.size()], x) q = qalloc(5) comp0 = test_compute_action1.extract_composite(q, 1.2345) print(comp0) # First and last instructions are CNOT's self.assertEqual(comp0.getInstruction(0).name(), "CNOT") self.assertEqual(comp0.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") comp1 = test_compute_action2.extract_composite(q, 1.2345) print(comp1) # First and last instructions are CNOT's self.assertEqual(comp1.getInstruction(0).name(), "CNOT") self.assertEqual(comp1.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") self.assertEqual(comp1.nInstructions(), comp0.nInstructions()) qq = qalloc(6) comp2 = test_compute_action3.extract_composite(qq, 1.2345) print(comp2) # First and last instructions are CNOT's self.assertEqual(comp2.getInstruction(0).name(), "CNOT") self.assertEqual(comp2.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") middle_inst_idx = (int) (comp2.nInstructions() / 2) # only Rz ==> CRz self.assertEqual(comp2.getInstruction(middle_inst_idx).name(), 'CRZ') self.assertEqual(comp2.nInstructions(), comp0.nInstructions()) comp3 = test_compute_action4.extract_composite(qq, 1.2345) print(comp3) # First and last instructions are CNOT's self.assertEqual(comp3.getInstruction(0).name(), "CNOT") self.assertEqual(comp3.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") middle_inst_idx = (int) (comp3.nInstructions() / 2) # only Rz ==> CRz self.assertEqual(comp3.getInstruction(middle_inst_idx).name(), 'CRZ') self.assertEqual(comp3.nInstructions(), comp0.nInstructions()) def test_multi_control(self): @qjit def x_gate_standalone_m(q: qubit): X(q) @qjit def test_compute_action1_m(q : qreg, x : float): with compute: # CCX X.ctrl(q[0:2], q[2]) with action: Rz(q[3], x) @qjit def test_compute_action2_m(q : qreg, x : float): with compute: # control of x gate wrapped in a kernel x_gate_standalone_m.ctrl(q[0:2], q[2]) with action: Rz(q[3], x) @qjit def test_compute_action3_m(q : qreg, x : float): # Control of a kernel compute-action (version 1: ctrl of simple gate) test_compute_action1_m.ctrl(q[0], q[1:q.size()], x) @qjit def test_compute_action4_m(q : qreg, x : float): # Control of a kernel compute-action (version 2: ctrl of a kernel) test_compute_action2_m.ctrl(q[0], q[1:q.size()], x) q = qalloc(5) comp0 = test_compute_action1_m.extract_composite(q, 1.2345) print(comp0) # 2 CCX and 1 Rz self.assertEqual(comp0.nInstructions(), 31) comp1 = test_compute_action2_m.extract_composite(q, 1.2345) self.assertEqual(comp1.nInstructions(), 31) comp2 = test_compute_action3_m.extract_composite(q, 1.2345) self.assertEqual(comp2.nInstructions(), 31) comp3 = test_compute_action4_m.extract_composite(q, 1.2345) self.assertEqual(comp3.nInstructions(), 31) # Check outer loop control self.assertEqual(comp0.getInstruction(15).name(), 'Rz') self.assertEqual(comp1.getInstruction(15).name(), 'Rz') self.assertEqual(comp2.getInstruction(15).name(), 'CRZ') self.assertEqual(comp3.getInstruction(15).name(), 'CRZ') if __name__ == '__main__': unittest.main() No newline at end of file runtime/kernel/quantum_kernel.hpp +133 −69 Original line number Diff line number Diff line Loading @@ -32,8 +32,7 @@ enum class QrtType { NISQ, FTQC }; // with an appropriate implementation of constructors and destructors. // Users can then call for adjoint/ctrl methods like this // foo::adjoint(q); foo::ctrl(1, q); template <typename Derived, typename... Args> class QuantumKernel { template <typename Derived, typename... Args> class QuantumKernel { protected: // Tuple holder for variadic kernel arguments std::tuple<Args...> args_tuple; Loading Loading @@ -66,8 +65,7 @@ class QuantumKernel { QuantumKernel(std::shared_ptr<qcor::CompositeInstruction> _parent_kernel, Args... args) : args_tuple(std::forward_as_tuple(args...)), parent_kernel(_parent_kernel), is_callable(false) { parent_kernel(_parent_kernel), is_callable(false) { runtime_env = (__qrt_env == "ftqc") ? QrtType::FTQC : QrtType::NISQ; } Loading Loading @@ -158,10 +156,25 @@ class QuantumKernel { Derived derived(args...); derived.disable_destructor = true; // Is is in a **compute** segment? // i.e. doing control within the compute block itself. // need to by-pass the compute marking in order for the control gate to // work. const bool cached_is_compute_section = ::quantum::qrt_impl->isComputeSection(); if (cached_is_compute_section) { ::quantum::qrt_impl->__end_mark_segment_as_compute(); } // run the operator()(args...) call to get the the functor // as a CompositeInstruction (derived.parent_kernel) // No compute markings on these instructions derived(args...); if (cached_is_compute_section) { ::quantum::qrt_impl->__begin_mark_segment_as_compute(); } // Use the controlled gate module of XACC to transform auto tempKernel = qcor::__internal__::create_composite("temp_control"); tempKernel->addInstruction(derived.parent_kernel); Loading @@ -172,10 +185,27 @@ class QuantumKernel { std::make_pair("control-idx", ctrlIdx), }); // Mark all the *Controlled* instructions as compute segment // if it was in the compute_section. // i.e. we have bypassed the marker previously to make C-U to work, // now we mark all the generated instructions. // e.g. // compute { // kernel::ctrl(....) //} // We disable compute flag to expand kernel then generate its control // circuit **then** mark compute for all instructions so that // later if we control the top-level kernel (containing this compute/action) // no controlling is needed for these instructions. if (cached_is_compute_section) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { ctrlKernel->getInstruction(instId)->attachMetadata( {{"__qcor__compute__segment__", true}}); } } // std::cout << "HELLO\n" << ctrlKernel->toString() << "\n"; for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { parent_kernel->addInstruction( ctrlKernel->getInstruction(instId)->clone()); parent_kernel->addInstruction(ctrlKernel->getInstruction(instId)); } // Need to reset and point current program to the parent quantum::set_current_program(parent_kernel); Loading Loading @@ -216,10 +246,24 @@ class QuantumKernel { Derived derived(args...); derived.disable_destructor = true; // Is is in a **compute** segment? // i.e. doing control within the compute block itself. // need to by-pass the compute marking in order for the control gate to // work. const bool cached_is_compute_section = ::quantum::qrt_impl->isComputeSection(); if (cached_is_compute_section) { ::quantum::qrt_impl->__end_mark_segment_as_compute(); } // run the operator()(args...) call to get the the functor // as a CompositeInstruction (derived.parent_kernel) derived(args...); if (cached_is_compute_section) { ::quantum::qrt_impl->__begin_mark_segment_as_compute(); } // Use the controlled gate module of XACC to transform auto tempKernel = qcor::__internal__::create_composite("temp_control"); tempKernel->addInstruction(derived.parent_kernel); Loading @@ -229,9 +273,19 @@ class QuantumKernel { {"control-idx", ctrl_bits}, {"control-buffer", buffer_name}}); // Mark all the *Controlled* instructions as compute segment // if it was in the compute_section. // i.e. we have bypassed the marker previously to make C-U to work, // now we mark all the generated instructions. if (cached_is_compute_section) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { ctrlKernel->getInstruction(instId)->attachMetadata( {{"__qcor__compute__segment__", true}}); } } for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { parent_kernel->addInstruction( ctrlKernel->getInstruction(instId)->clone()); parent_kernel->addInstruction(ctrlKernel->getInstruction(instId)); } // Need to reset and point current program to the parent quantum::set_current_program(parent_kernel); Loading @@ -246,10 +300,25 @@ class QuantumKernel { Derived derived(args...); derived.disable_destructor = true; // Is is in a **compute** segment? // i.e. doing control within the compute block itself. // need to by-pass the compute marking in order for the control gate to // work. const bool cached_is_compute_section = ::quantum::qrt_impl->isComputeSection(); if (cached_is_compute_section) { ::quantum::qrt_impl->__end_mark_segment_as_compute(); } // run the operator()(args...) call to get the the functor // as a CompositeInstruction (derived.parent_kernel) // No compute markings on these instructions derived(args...); if (cached_is_compute_section) { ::quantum::qrt_impl->__begin_mark_segment_as_compute(); } // Use the controlled gate module of XACC to transform auto tempKernel = qcor::__internal__::create_composite("temp_control"); tempKernel->addInstruction(derived.parent_kernel); Loading @@ -259,9 +328,19 @@ class QuantumKernel { {"control-idx", ctrl_bit}, {"control-buffer", ctrl_qbit.first}}); // Mark all the *Controlled* instructions as compute segment // if it was in the compute_section. // i.e. we have bypassed the marker previously to make C-U to work, // now we mark all the generated instructions. if (cached_is_compute_section) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { ctrlKernel->getInstruction(instId)->attachMetadata( {{"__qcor__compute__segment__", true}}); } } for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { parent_kernel->addInstruction( ctrlKernel->getInstruction(instId)->clone()); parent_kernel->addInstruction(ctrlKernel->getInstruction(instId)); } // Need to reset and point current program to the parent quantum::set_current_program(parent_kernel); Loading Loading @@ -364,15 +443,7 @@ using OneQubitKernel = QuantumKernel<Derived, qubit>; if (runtime_env == QrtType::FTQC) { \ quantum::set_current_buffer(q.results()); \ } \ bool cached_is_compute_section = \ ::quantum::qrt_impl->isComputeSection(); \ if (cached_is_compute_section) { \ ::quantum::qrt_impl->__end_mark_segment_as_compute(); \ } \ ::quantum::QRTNAME(q); \ if (cached_is_compute_section) { \ ::quantum::qrt_impl->__begin_mark_segment_as_compute(); \ } \ return; \ } \ virtual ~CLASSNAME() {} \ Loading Loading @@ -400,8 +471,7 @@ ONE_QUBIT_KERNEL_CTRL_ENABLER(Sdg, sdg) // trailing variadic argument for the lambda class constructor. Once // instantiated lambda invocation looks just like kernel invocation. template <typename... CaptureArgs> class _qpu_lambda { template <typename... CaptureArgs> class _qpu_lambda { private: // Private inner class for getting the type // of a capture variable as a string at runtime Loading @@ -411,8 +481,7 @@ class _qpu_lambda { std::vector<std::string> var_names; int counter = 0; template <class T> std::string type_name() { template <class T> std::string type_name() { typedef typename std::remove_reference<T>::type TR; std::unique_ptr<char, void (*)(void *)> own( abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr), Loading @@ -425,8 +494,7 @@ class _qpu_lambda { TupleToTypeArgString(std::string &t) : tmp(t) {} TupleToTypeArgString(std::string &t, std::vector<std::string> &_var_names) : tmp(t), var_names(_var_names) {} template <typename T> void operator()(T &t) { template <typename T> void operator()(T &t) { tmp += type_name<decltype(t)>() + " " + (var_names.empty() ? "arg_" + std::to_string(counter) : var_names[counter]) + Loading @@ -452,8 +520,7 @@ class _qpu_lambda { // specifying them since we're using C++17 _qpu_lambda(std::string &&ff, std::string &&_capture_var_names, CaptureArgs &... _capture_vars) : src_str(ff), capture_var_names(_capture_var_names), : src_str(ff), capture_var_names(_capture_var_names), capture_vars(std::forward_as_tuple(_capture_vars...)) { // Get the original args list auto first = src_str.find_first_of("("); Loading Loading @@ -490,7 +557,8 @@ class _qpu_lambda { // preamble if necessary auto jit_src = ss.str(); first = jit_src.find_first_of("{"); if (!capture_var_names.empty()) jit_src.insert(first + 1, capture_preamble); if (!capture_var_names.empty()) jit_src.insert(first + 1, capture_preamble); // std::cout << "JITSRC:\n" << jit_src << "\n"; // JIT Compile, storing the function pointers Loading Loading @@ -518,8 +586,7 @@ class _qpu_lambda { final_args_tuple); } template <typename... FunctionArgs> void operator()(FunctionArgs... args) { template <typename... FunctionArgs> void operator()(FunctionArgs... args) { // Map the function args to a tuple auto kernel_args_tuple = std::make_tuple(args...); Loading Loading @@ -563,8 +630,7 @@ template <typename... Args> using callable_function_ptr = void (*)(std::shared_ptr<xacc::CompositeInstruction>, Args...); template <typename... Args> class KernelSignature { template <typename... Args> class KernelSignature { private: callable_function_ptr<Args...> *readOnly = 0; callable_function_ptr<Args...> &function_pointer; Loading @@ -572,13 +638,11 @@ class KernelSignature { lambda_func; public: // Here we set function_pointer to null and instead // only use lambda_func. If we set lambda_func, function_pointer // will never be used, so we should be good. template <typename... CaptureArgs> KernelSignature( _qpu_lambda<CaptureArgs...> &lambda) KernelSignature(_qpu_lambda<CaptureArgs...> &lambda) : function_pointer(*readOnly), lambda_func([&](std::shared_ptr<xacc::CompositeInstruction> pp, Args... a) { lambda(pp, a...); }) {} Loading Loading
python/tests/CMakeLists.txt +7 −0 Original line number Diff line number Diff line Loading @@ -74,3 +74,10 @@ add_test (NAME qcor_python_jit_kernel_signature set_tests_properties(qcor_python_jit_kernel_signature PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_INSTALL_PREFIX}:$ENV{PYTHONPATH}") add_test (NAME qcor_python_compute_action COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_compute_action.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) set_tests_properties(qcor_python_compute_action PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_INSTALL_PREFIX}:$ENV{PYTHONPATH}") No newline at end of file
python/tests/test_compute_action.py 0 → 100644 +129 −0 Original line number Diff line number Diff line import faulthandler faulthandler.enable() import unittest from qcor import * class TestKernelJIT(unittest.TestCase): def test_simple(self): @qjit def x_gate_standalone(q: qubit): X(q) @qjit def test_compute_action1(q : qreg, x : float): with compute: # control of x gate X.ctrl(q[0], q[1]) for i in range(3): H(q[i+1]) with action: Rz(q[3], x) @qjit def test_compute_action2(q : qreg, x : float): with compute: # control of x gate wrapped in a kernel x_gate_standalone.ctrl(q[0], q[1]) for i in range(3): H(q[i+1]) with action: Rz(q[3], x) @qjit def test_compute_action3(q : qreg, x : float): # Control of a kernel compute-action (version 1: ctrl of simple gate) test_compute_action1.ctrl(q[0], q[1:q.size()], x) @qjit def test_compute_action4(q : qreg, x : float): # Control of a kernel compute-action (version 2: ctrl of a kernel) test_compute_action2.ctrl(q[0], q[1:q.size()], x) q = qalloc(5) comp0 = test_compute_action1.extract_composite(q, 1.2345) print(comp0) # First and last instructions are CNOT's self.assertEqual(comp0.getInstruction(0).name(), "CNOT") self.assertEqual(comp0.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") comp1 = test_compute_action2.extract_composite(q, 1.2345) print(comp1) # First and last instructions are CNOT's self.assertEqual(comp1.getInstruction(0).name(), "CNOT") self.assertEqual(comp1.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") self.assertEqual(comp1.nInstructions(), comp0.nInstructions()) qq = qalloc(6) comp2 = test_compute_action3.extract_composite(qq, 1.2345) print(comp2) # First and last instructions are CNOT's self.assertEqual(comp2.getInstruction(0).name(), "CNOT") self.assertEqual(comp2.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") middle_inst_idx = (int) (comp2.nInstructions() / 2) # only Rz ==> CRz self.assertEqual(comp2.getInstruction(middle_inst_idx).name(), 'CRZ') self.assertEqual(comp2.nInstructions(), comp0.nInstructions()) comp3 = test_compute_action4.extract_composite(qq, 1.2345) print(comp3) # First and last instructions are CNOT's self.assertEqual(comp3.getInstruction(0).name(), "CNOT") self.assertEqual(comp3.getInstruction(comp0.nInstructions() - 1).name(), "CNOT") middle_inst_idx = (int) (comp3.nInstructions() / 2) # only Rz ==> CRz self.assertEqual(comp3.getInstruction(middle_inst_idx).name(), 'CRZ') self.assertEqual(comp3.nInstructions(), comp0.nInstructions()) def test_multi_control(self): @qjit def x_gate_standalone_m(q: qubit): X(q) @qjit def test_compute_action1_m(q : qreg, x : float): with compute: # CCX X.ctrl(q[0:2], q[2]) with action: Rz(q[3], x) @qjit def test_compute_action2_m(q : qreg, x : float): with compute: # control of x gate wrapped in a kernel x_gate_standalone_m.ctrl(q[0:2], q[2]) with action: Rz(q[3], x) @qjit def test_compute_action3_m(q : qreg, x : float): # Control of a kernel compute-action (version 1: ctrl of simple gate) test_compute_action1_m.ctrl(q[0], q[1:q.size()], x) @qjit def test_compute_action4_m(q : qreg, x : float): # Control of a kernel compute-action (version 2: ctrl of a kernel) test_compute_action2_m.ctrl(q[0], q[1:q.size()], x) q = qalloc(5) comp0 = test_compute_action1_m.extract_composite(q, 1.2345) print(comp0) # 2 CCX and 1 Rz self.assertEqual(comp0.nInstructions(), 31) comp1 = test_compute_action2_m.extract_composite(q, 1.2345) self.assertEqual(comp1.nInstructions(), 31) comp2 = test_compute_action3_m.extract_composite(q, 1.2345) self.assertEqual(comp2.nInstructions(), 31) comp3 = test_compute_action4_m.extract_composite(q, 1.2345) self.assertEqual(comp3.nInstructions(), 31) # Check outer loop control self.assertEqual(comp0.getInstruction(15).name(), 'Rz') self.assertEqual(comp1.getInstruction(15).name(), 'Rz') self.assertEqual(comp2.getInstruction(15).name(), 'CRZ') self.assertEqual(comp3.getInstruction(15).name(), 'CRZ') if __name__ == '__main__': unittest.main() No newline at end of file
runtime/kernel/quantum_kernel.hpp +133 −69 Original line number Diff line number Diff line Loading @@ -32,8 +32,7 @@ enum class QrtType { NISQ, FTQC }; // with an appropriate implementation of constructors and destructors. // Users can then call for adjoint/ctrl methods like this // foo::adjoint(q); foo::ctrl(1, q); template <typename Derived, typename... Args> class QuantumKernel { template <typename Derived, typename... Args> class QuantumKernel { protected: // Tuple holder for variadic kernel arguments std::tuple<Args...> args_tuple; Loading Loading @@ -66,8 +65,7 @@ class QuantumKernel { QuantumKernel(std::shared_ptr<qcor::CompositeInstruction> _parent_kernel, Args... args) : args_tuple(std::forward_as_tuple(args...)), parent_kernel(_parent_kernel), is_callable(false) { parent_kernel(_parent_kernel), is_callable(false) { runtime_env = (__qrt_env == "ftqc") ? QrtType::FTQC : QrtType::NISQ; } Loading Loading @@ -158,10 +156,25 @@ class QuantumKernel { Derived derived(args...); derived.disable_destructor = true; // Is is in a **compute** segment? // i.e. doing control within the compute block itself. // need to by-pass the compute marking in order for the control gate to // work. const bool cached_is_compute_section = ::quantum::qrt_impl->isComputeSection(); if (cached_is_compute_section) { ::quantum::qrt_impl->__end_mark_segment_as_compute(); } // run the operator()(args...) call to get the the functor // as a CompositeInstruction (derived.parent_kernel) // No compute markings on these instructions derived(args...); if (cached_is_compute_section) { ::quantum::qrt_impl->__begin_mark_segment_as_compute(); } // Use the controlled gate module of XACC to transform auto tempKernel = qcor::__internal__::create_composite("temp_control"); tempKernel->addInstruction(derived.parent_kernel); Loading @@ -172,10 +185,27 @@ class QuantumKernel { std::make_pair("control-idx", ctrlIdx), }); // Mark all the *Controlled* instructions as compute segment // if it was in the compute_section. // i.e. we have bypassed the marker previously to make C-U to work, // now we mark all the generated instructions. // e.g. // compute { // kernel::ctrl(....) //} // We disable compute flag to expand kernel then generate its control // circuit **then** mark compute for all instructions so that // later if we control the top-level kernel (containing this compute/action) // no controlling is needed for these instructions. if (cached_is_compute_section) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { ctrlKernel->getInstruction(instId)->attachMetadata( {{"__qcor__compute__segment__", true}}); } } // std::cout << "HELLO\n" << ctrlKernel->toString() << "\n"; for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { parent_kernel->addInstruction( ctrlKernel->getInstruction(instId)->clone()); parent_kernel->addInstruction(ctrlKernel->getInstruction(instId)); } // Need to reset and point current program to the parent quantum::set_current_program(parent_kernel); Loading Loading @@ -216,10 +246,24 @@ class QuantumKernel { Derived derived(args...); derived.disable_destructor = true; // Is is in a **compute** segment? // i.e. doing control within the compute block itself. // need to by-pass the compute marking in order for the control gate to // work. const bool cached_is_compute_section = ::quantum::qrt_impl->isComputeSection(); if (cached_is_compute_section) { ::quantum::qrt_impl->__end_mark_segment_as_compute(); } // run the operator()(args...) call to get the the functor // as a CompositeInstruction (derived.parent_kernel) derived(args...); if (cached_is_compute_section) { ::quantum::qrt_impl->__begin_mark_segment_as_compute(); } // Use the controlled gate module of XACC to transform auto tempKernel = qcor::__internal__::create_composite("temp_control"); tempKernel->addInstruction(derived.parent_kernel); Loading @@ -229,9 +273,19 @@ class QuantumKernel { {"control-idx", ctrl_bits}, {"control-buffer", buffer_name}}); // Mark all the *Controlled* instructions as compute segment // if it was in the compute_section. // i.e. we have bypassed the marker previously to make C-U to work, // now we mark all the generated instructions. if (cached_is_compute_section) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { ctrlKernel->getInstruction(instId)->attachMetadata( {{"__qcor__compute__segment__", true}}); } } for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { parent_kernel->addInstruction( ctrlKernel->getInstruction(instId)->clone()); parent_kernel->addInstruction(ctrlKernel->getInstruction(instId)); } // Need to reset and point current program to the parent quantum::set_current_program(parent_kernel); Loading @@ -246,10 +300,25 @@ class QuantumKernel { Derived derived(args...); derived.disable_destructor = true; // Is is in a **compute** segment? // i.e. doing control within the compute block itself. // need to by-pass the compute marking in order for the control gate to // work. const bool cached_is_compute_section = ::quantum::qrt_impl->isComputeSection(); if (cached_is_compute_section) { ::quantum::qrt_impl->__end_mark_segment_as_compute(); } // run the operator()(args...) call to get the the functor // as a CompositeInstruction (derived.parent_kernel) // No compute markings on these instructions derived(args...); if (cached_is_compute_section) { ::quantum::qrt_impl->__begin_mark_segment_as_compute(); } // Use the controlled gate module of XACC to transform auto tempKernel = qcor::__internal__::create_composite("temp_control"); tempKernel->addInstruction(derived.parent_kernel); Loading @@ -259,9 +328,19 @@ class QuantumKernel { {"control-idx", ctrl_bit}, {"control-buffer", ctrl_qbit.first}}); // Mark all the *Controlled* instructions as compute segment // if it was in the compute_section. // i.e. we have bypassed the marker previously to make C-U to work, // now we mark all the generated instructions. if (cached_is_compute_section) { for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { ctrlKernel->getInstruction(instId)->attachMetadata( {{"__qcor__compute__segment__", true}}); } } for (int instId = 0; instId < ctrlKernel->nInstructions(); ++instId) { parent_kernel->addInstruction( ctrlKernel->getInstruction(instId)->clone()); parent_kernel->addInstruction(ctrlKernel->getInstruction(instId)); } // Need to reset and point current program to the parent quantum::set_current_program(parent_kernel); Loading Loading @@ -364,15 +443,7 @@ using OneQubitKernel = QuantumKernel<Derived, qubit>; if (runtime_env == QrtType::FTQC) { \ quantum::set_current_buffer(q.results()); \ } \ bool cached_is_compute_section = \ ::quantum::qrt_impl->isComputeSection(); \ if (cached_is_compute_section) { \ ::quantum::qrt_impl->__end_mark_segment_as_compute(); \ } \ ::quantum::QRTNAME(q); \ if (cached_is_compute_section) { \ ::quantum::qrt_impl->__begin_mark_segment_as_compute(); \ } \ return; \ } \ virtual ~CLASSNAME() {} \ Loading Loading @@ -400,8 +471,7 @@ ONE_QUBIT_KERNEL_CTRL_ENABLER(Sdg, sdg) // trailing variadic argument for the lambda class constructor. Once // instantiated lambda invocation looks just like kernel invocation. template <typename... CaptureArgs> class _qpu_lambda { template <typename... CaptureArgs> class _qpu_lambda { private: // Private inner class for getting the type // of a capture variable as a string at runtime Loading @@ -411,8 +481,7 @@ class _qpu_lambda { std::vector<std::string> var_names; int counter = 0; template <class T> std::string type_name() { template <class T> std::string type_name() { typedef typename std::remove_reference<T>::type TR; std::unique_ptr<char, void (*)(void *)> own( abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr), Loading @@ -425,8 +494,7 @@ class _qpu_lambda { TupleToTypeArgString(std::string &t) : tmp(t) {} TupleToTypeArgString(std::string &t, std::vector<std::string> &_var_names) : tmp(t), var_names(_var_names) {} template <typename T> void operator()(T &t) { template <typename T> void operator()(T &t) { tmp += type_name<decltype(t)>() + " " + (var_names.empty() ? "arg_" + std::to_string(counter) : var_names[counter]) + Loading @@ -452,8 +520,7 @@ class _qpu_lambda { // specifying them since we're using C++17 _qpu_lambda(std::string &&ff, std::string &&_capture_var_names, CaptureArgs &... _capture_vars) : src_str(ff), capture_var_names(_capture_var_names), : src_str(ff), capture_var_names(_capture_var_names), capture_vars(std::forward_as_tuple(_capture_vars...)) { // Get the original args list auto first = src_str.find_first_of("("); Loading Loading @@ -490,7 +557,8 @@ class _qpu_lambda { // preamble if necessary auto jit_src = ss.str(); first = jit_src.find_first_of("{"); if (!capture_var_names.empty()) jit_src.insert(first + 1, capture_preamble); if (!capture_var_names.empty()) jit_src.insert(first + 1, capture_preamble); // std::cout << "JITSRC:\n" << jit_src << "\n"; // JIT Compile, storing the function pointers Loading Loading @@ -518,8 +586,7 @@ class _qpu_lambda { final_args_tuple); } template <typename... FunctionArgs> void operator()(FunctionArgs... args) { template <typename... FunctionArgs> void operator()(FunctionArgs... args) { // Map the function args to a tuple auto kernel_args_tuple = std::make_tuple(args...); Loading Loading @@ -563,8 +630,7 @@ template <typename... Args> using callable_function_ptr = void (*)(std::shared_ptr<xacc::CompositeInstruction>, Args...); template <typename... Args> class KernelSignature { template <typename... Args> class KernelSignature { private: callable_function_ptr<Args...> *readOnly = 0; callable_function_ptr<Args...> &function_pointer; Loading @@ -572,13 +638,11 @@ class KernelSignature { lambda_func; public: // Here we set function_pointer to null and instead // only use lambda_func. If we set lambda_func, function_pointer // will never be used, so we should be good. template <typename... CaptureArgs> KernelSignature( _qpu_lambda<CaptureArgs...> &lambda) KernelSignature(_qpu_lambda<CaptureArgs...> &lambda) : function_pointer(*readOnly), lambda_func([&](std::shared_ptr<xacc::CompositeInstruction> pp, Args... a) { lambda(pp, a...); }) {} Loading