Loading examples/chemistry/simple_h2_test_ucc1.cpp 0 → 100644 +29 −0 Original line number Diff line number Diff line __qpu__ void ansatz(qreg q, double theta) { X(q[0]); X(q[2]); compute { Rx(q[0], constants::pi / 2); for (auto i : range(3)) H(q[i + 1]); for (auto i : range(3)) { CX(q[i], q[i + 1]); } } action { Rz(q[3], theta); } } int main() { std::string h2_geom = R"#(H 0.000000 0.0 0.0 H 0.0 0.0 .7474)#"; auto H = createOperator("pyscf", {{"basis", "sto-3g"}, {"geometry", h2_geom}}); OptFunction opt_function( [&](std::vector<double> x) { return ansatz::observe(H, qalloc(4), x[0]); }, 1); auto [energy, opt_params] = createOptimizer("nlopt")->optimize(opt_function); print(energy); } No newline at end of file runtime/kernel/quantum_kernel.hpp +104 −65 Original line number Diff line number Diff line #pragma once #include <optional> #include "qcor_jit.hpp" #include "qcor_observable.hpp" #include "qcor_utils.hpp" #include "qrt.hpp" #include <optional> namespace qcor { enum class QrtType { NISQ, FTQC }; // Forward declare template <typename... Args> class KernelSignature; template <typename... Args> class KernelSignature; namespace internal { // KernelSignature is the base of all kernel-like objects Loading Loading @@ -67,7 +69,8 @@ std::size_t n_instructions(KernelSignature<Args...> &kernelCallable, // 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 @@ -100,7 +103,8 @@ public: 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 @@ -191,7 +195,8 @@ public: virtual ~QuantumKernel() {} template <typename... ArgTypes> friend class KernelSignature; template <typename... ArgTypes> friend class KernelSignature; }; // We use the following to enable ctrl operations on our single Loading Loading @@ -243,7 +248,8 @@ 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 @@ -253,7 +259,8 @@ private: 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 @@ -266,7 +273,8 @@ private: 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 Loading @@ -309,7 +317,8 @@ public: // 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 @@ -364,7 +373,6 @@ public: return result; }(tt); // Determine if this lambda has a VQE-compatible type: // QReg then variational params. if (arg_type_and_names.size() == 2) { Loading Loading @@ -397,8 +405,7 @@ public: // i.e. by-value arguments of these types are incompatible with a by-ref // casted function. static const std::unordered_map<std::string, std::string> FORWARD_TYPE_CONVERSION_MAP{{"int", "int&"}, {"double", "double&"}}; FORWARD_TYPE_CONVERSION_MAP{{"int", "int&"}, {"double", "double&"}}; std::vector<std::pair<std::string, std::string>> forward_types; // Replicate by-value by create copies and restore the variables. std::vector<std::string> byval_casted_arg_names; Loading Loading @@ -446,7 +453,8 @@ public: // Store capture vars (by-value) optional_copy_capture_vars = std::forward_as_tuple(_capture_vars...); } else { error("Capture variable type is non-copyable. Cannot use capture by " error( "Capture variable type is non-copyable. Cannot use capture by " "value."); } } Loading Loading @@ -496,8 +504,7 @@ public: // 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); if (!byval_casted_arg_names.empty()) { std::stringstream cache_string, restore_string; Loading Loading @@ -552,7 +559,8 @@ public: } } 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::forward_as_tuple(args...); if (!optional_copy_capture_vars.has_value()) { Loading Loading @@ -637,7 +645,8 @@ public: return internal::print_kernel(callable, os, args...); } template <typename... FunctionArgs> void print_kernel(FunctionArgs... args) { template <typename... FunctionArgs> void print_kernel(FunctionArgs... args) { print_kernel(std::cout, args...); } Loading Loading @@ -666,7 +675,8 @@ 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 Loading @@ -918,10 +928,38 @@ double observe(Observable &obs, KernelSignature<Args...> &kernelCallable, xacc::internal_compiler::execute_pass_manager(); // Operator pre-processing, map down to Pauli and cache std::shared_ptr<Observable> observable; auto obs_str = obs.toString(); std::hash<std::string> hasher; auto operator_hash = hasher(obs_str); if (__internal__::cached_observables.count(operator_hash)) { observable = __internal__::cached_observables[operator_hash]; } else { if (obs_str.find("^") != std::string::npos) { try { observable = operatorTransform("jw", dynamic_cast<FermionOperator &>(obs)); } catch (std::exception &ex) { auto fermionObservable = createOperator("fermion", obs_str); observable = operatorTransform("jw", fermionObservable); } // observable is PauliOperator, but does not cast down to it // Not sure about the likelihood of this happening, but want to cover all // bases } else if (obs_str.find("X") != std::string::npos || obs_str.find("Y") != std::string::npos || obs_str.find("Z") != std::string::npos) { observable = createOperator("pauli", obs_str); } __internal__::cached_observables.insert({operator_hash, observable}); } // Will fail to compile if more than one qreg is passed. std::tuple<Args...> tmp(std::forward_as_tuple(args...)); auto q = std::get<qreg>(tmp); return qcor::observe(tempKernel, obs, q); return qcor::observe(tempKernel, observable, q); } template <typename... Args> Loading @@ -934,7 +972,8 @@ Eigen::MatrixXcd as_unitary_matrix(KernelSignature<Args...> &kernelCallable, if (!std::all_of( instructions.cbegin(), instructions.cend(), [](const auto &inst) { return inst->name() != "Measure"; })) { error("Unable to compute unitary matrix for kernels that already have " error( "Unable to compute unitary matrix for kernels that already have " "Measure operations."); } qcor::KernelToUnitaryVisitor visitor(tempKernel->nLogicalBits()); Loading runtime/observable/qcor_observable.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,7 @@ std::shared_ptr<Observable> _internal_python_createObservable( } namespace __internal__ { std::map<std::size_t, std::shared_ptr<Observable>> cached_observables = {}; std::vector<std::shared_ptr<xacc::CompositeInstruction>> observe( std::shared_ptr<xacc::Observable> obs, Loading runtime/observable/qcor_observable.hpp +2 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,8 @@ std::vector<std::shared_ptr<CompositeInstruction>> observe( std::shared_ptr<Observable> obs, std::shared_ptr<CompositeInstruction> program); extern std::map<std::size_t, std::shared_ptr<Observable>> cached_observables; } // namespace __internal__ std::shared_ptr<Observable> _internal_python_createObservable( Loading Loading
examples/chemistry/simple_h2_test_ucc1.cpp 0 → 100644 +29 −0 Original line number Diff line number Diff line __qpu__ void ansatz(qreg q, double theta) { X(q[0]); X(q[2]); compute { Rx(q[0], constants::pi / 2); for (auto i : range(3)) H(q[i + 1]); for (auto i : range(3)) { CX(q[i], q[i + 1]); } } action { Rz(q[3], theta); } } int main() { std::string h2_geom = R"#(H 0.000000 0.0 0.0 H 0.0 0.0 .7474)#"; auto H = createOperator("pyscf", {{"basis", "sto-3g"}, {"geometry", h2_geom}}); OptFunction opt_function( [&](std::vector<double> x) { return ansatz::observe(H, qalloc(4), x[0]); }, 1); auto [energy, opt_params] = createOptimizer("nlopt")->optimize(opt_function); print(energy); } No newline at end of file
runtime/kernel/quantum_kernel.hpp +104 −65 Original line number Diff line number Diff line #pragma once #include <optional> #include "qcor_jit.hpp" #include "qcor_observable.hpp" #include "qcor_utils.hpp" #include "qrt.hpp" #include <optional> namespace qcor { enum class QrtType { NISQ, FTQC }; // Forward declare template <typename... Args> class KernelSignature; template <typename... Args> class KernelSignature; namespace internal { // KernelSignature is the base of all kernel-like objects Loading Loading @@ -67,7 +69,8 @@ std::size_t n_instructions(KernelSignature<Args...> &kernelCallable, // 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 @@ -100,7 +103,8 @@ public: 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 @@ -191,7 +195,8 @@ public: virtual ~QuantumKernel() {} template <typename... ArgTypes> friend class KernelSignature; template <typename... ArgTypes> friend class KernelSignature; }; // We use the following to enable ctrl operations on our single Loading Loading @@ -243,7 +248,8 @@ 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 @@ -253,7 +259,8 @@ private: 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 @@ -266,7 +273,8 @@ private: 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 Loading @@ -309,7 +317,8 @@ public: // 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 @@ -364,7 +373,6 @@ public: return result; }(tt); // Determine if this lambda has a VQE-compatible type: // QReg then variational params. if (arg_type_and_names.size() == 2) { Loading Loading @@ -397,8 +405,7 @@ public: // i.e. by-value arguments of these types are incompatible with a by-ref // casted function. static const std::unordered_map<std::string, std::string> FORWARD_TYPE_CONVERSION_MAP{{"int", "int&"}, {"double", "double&"}}; FORWARD_TYPE_CONVERSION_MAP{{"int", "int&"}, {"double", "double&"}}; std::vector<std::pair<std::string, std::string>> forward_types; // Replicate by-value by create copies and restore the variables. std::vector<std::string> byval_casted_arg_names; Loading Loading @@ -446,7 +453,8 @@ public: // Store capture vars (by-value) optional_copy_capture_vars = std::forward_as_tuple(_capture_vars...); } else { error("Capture variable type is non-copyable. Cannot use capture by " error( "Capture variable type is non-copyable. Cannot use capture by " "value."); } } Loading Loading @@ -496,8 +504,7 @@ public: // 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); if (!byval_casted_arg_names.empty()) { std::stringstream cache_string, restore_string; Loading Loading @@ -552,7 +559,8 @@ public: } } 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::forward_as_tuple(args...); if (!optional_copy_capture_vars.has_value()) { Loading Loading @@ -637,7 +645,8 @@ public: return internal::print_kernel(callable, os, args...); } template <typename... FunctionArgs> void print_kernel(FunctionArgs... args) { template <typename... FunctionArgs> void print_kernel(FunctionArgs... args) { print_kernel(std::cout, args...); } Loading Loading @@ -666,7 +675,8 @@ 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 Loading @@ -918,10 +928,38 @@ double observe(Observable &obs, KernelSignature<Args...> &kernelCallable, xacc::internal_compiler::execute_pass_manager(); // Operator pre-processing, map down to Pauli and cache std::shared_ptr<Observable> observable; auto obs_str = obs.toString(); std::hash<std::string> hasher; auto operator_hash = hasher(obs_str); if (__internal__::cached_observables.count(operator_hash)) { observable = __internal__::cached_observables[operator_hash]; } else { if (obs_str.find("^") != std::string::npos) { try { observable = operatorTransform("jw", dynamic_cast<FermionOperator &>(obs)); } catch (std::exception &ex) { auto fermionObservable = createOperator("fermion", obs_str); observable = operatorTransform("jw", fermionObservable); } // observable is PauliOperator, but does not cast down to it // Not sure about the likelihood of this happening, but want to cover all // bases } else if (obs_str.find("X") != std::string::npos || obs_str.find("Y") != std::string::npos || obs_str.find("Z") != std::string::npos) { observable = createOperator("pauli", obs_str); } __internal__::cached_observables.insert({operator_hash, observable}); } // Will fail to compile if more than one qreg is passed. std::tuple<Args...> tmp(std::forward_as_tuple(args...)); auto q = std::get<qreg>(tmp); return qcor::observe(tempKernel, obs, q); return qcor::observe(tempKernel, observable, q); } template <typename... Args> Loading @@ -934,7 +972,8 @@ Eigen::MatrixXcd as_unitary_matrix(KernelSignature<Args...> &kernelCallable, if (!std::all_of( instructions.cbegin(), instructions.cend(), [](const auto &inst) { return inst->name() != "Measure"; })) { error("Unable to compute unitary matrix for kernels that already have " error( "Unable to compute unitary matrix for kernels that already have " "Measure operations."); } qcor::KernelToUnitaryVisitor visitor(tempKernel->nLogicalBits()); Loading
runtime/observable/qcor_observable.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -130,6 +130,7 @@ std::shared_ptr<Observable> _internal_python_createObservable( } namespace __internal__ { std::map<std::size_t, std::shared_ptr<Observable>> cached_observables = {}; std::vector<std::shared_ptr<xacc::CompositeInstruction>> observe( std::shared_ptr<xacc::Observable> obs, Loading
runtime/observable/qcor_observable.hpp +2 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,8 @@ std::vector<std::shared_ptr<CompositeInstruction>> observe( std::shared_ptr<Observable> obs, std::shared_ptr<CompositeInstruction> program); extern std::map<std::size_t, std::shared_ptr<Observable>> cached_observables; } // namespace __internal__ std::shared_ptr<Observable> _internal_python_createObservable( Loading