Loading handlers/qcor_syntax_handler.cpp +151 −67 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ namespace { bool qrt = false; std::string qpu_name = "qpp"; int shots = 0; int shots = 1024; class QCORSyntaxHandler : public SyntaxHandler { public: Loading @@ -24,8 +24,6 @@ public: void GetReplacement(Preprocessor &PP, Declarator &D, CachedTokens &Toks, llvm::raw_string_ostream &OS) override { // FIXME need way to get backend name from user/command line // Get the Diagnostics engine and create a few custom // error messgaes auto &diagnostics = PP.getDiagnostics(); Loading Loading @@ -89,70 +87,121 @@ public: auto new_src = qcor::run_token_collector(PP, Toks, bufferNames); OS << function_prototype << "{\n"; OS << "quantum::initialize(\"" << qpu_name << "\", \"" << kernel_name << "\");\n"; for (auto &buf : bufferNames) { OS << buf << ".setNameAndStore(\"" + buf + "\");\n"; // First re-write, forward declare a function // we will implement further down OS << "void __internal_call_function_" << kernel_name << "(" << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << ");\n"; if (shots > 0) { OS << "quantum::set_shots(" << shots << ");\n"; // Call that forward declared function with the function args OS << "__internal_call_function_" << kernel_name << "(" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << new_src; OS << "if (__execute) {\n"; OS << ");\n"; if (bufferNames.size() > 1) { OS << "xacc::AcceleratorBuffer * buffers[" << bufferNames.size() << "] = {"; OS << bufferNames[0] << ".results()"; for (unsigned int k = 1; k < bufferNames.size(); k++) { OS << ", " << bufferNames[k] << ".results()"; } OS << "};\n"; OS << "std::cout << \"execing: \" << quantum::getProgram()->toString() " "<< \"\\n\";\n"; // Close the transformed function OS << "}\n"; OS << "quantum::submit(buffers," << bufferNames.size(); } else { OS << "quantum::submit(" << bufferNames[0] << ".results()"; // Declare the QuantumKernel subclass OS << "class " << kernel_name << " : public qcor::QuantumKernel<class " << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << "> {\n"; OS << ");\n"; OS << "}"; OS << "\n}\n"; OS << "class " << kernel_name << "{\n"; OS << "public:\n"; OS << "static void adjoint("; for (int i = 0; i < program_arg_types.size(); i++) { if (i > 0) { OS << ","; // declare the super-type as a friend OS << "friend class qcor::QuantumKernel<class " << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } auto arg_type = program_arg_types[i]; auto arg_var = program_parameters[i]; OS << arg_type << " " << arg_var; OS << ">;\n"; // declare protected operator()() method OS << "protected:\n"; OS << "void operator()(" << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << ") {\n"; OS << "if (!parent_kernel) {\n"; OS << "parent_kernel = " "qcor::__internal__::create_composite(kernel_name);\n"; OS << "// q.setNameAndStore();\n"; OS << "}\n"; OS << "quantum::set_current_program(parent_kernel);\n"; OS << new_src << "\n"; OS << "}\n"; OS << "quantum::initialize(\"" << qpu_name << "\", \"" << kernel_name << "\");\n"; for (auto &buf : bufferNames) { OS << buf << ".setNameAndStore(\"" + buf + "\");\n"; // declare public members, methods, constructors OS << "public:\n"; OS << "inline static const std::string kernel_name = \"" << kernel_name << "\";\n"; // First constructor, default one KERNEL_NAME(Args...); OS << kernel_name << "(" << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } if (shots > 0) { OS << "quantum::set_shots(" << shots << ");\n"; OS << "): QuantumKernel<" << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << "> (" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ") {}\n"; // Second constructor, takes parent CompositeInstruction // KERNEL_NAME(CompositeInstruction, Args...) OS << kernel_name << "(std::shared_ptr<qcor::CompositeInstruction> _parent, " << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << "): QuantumKernel<" << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << "> (_parent, " << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ") {}\n"; OS << new_src; OS << "quantum::adjoint();\n"; OS << "if (__execute) {\n"; // Third constructor, nullary constructor OS << kernel_name << "() : QuantumKernel<" << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << ">() {}\n"; // Destructor definition OS << "virtual ~" << kernel_name << "() {\n"; OS << "if (disable_destructor) {return;}\n"; OS << "quantum::set_backend(\"" << qpu_name << "\", " << shots << ");\n"; OS << "auto [" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << "] = args_tuple;\n"; OS << "operator()(" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ");\n"; OS << "if (is_callable) {\n"; if (bufferNames.size() > 1) { OS << "xacc::AcceleratorBuffer * buffers[" << bufferNames.size() << "] = {"; Loading @@ -166,13 +215,42 @@ public: OS << "quantum::submit(" << bufferNames[0] << ".results()"; } OS << ");\n"; OS << "}\n"; OS << "}\n"; // close the quantum kernel subclass OS << "};\n"; // Add a function with the kernel_name that takes // a parent CompositeInstruction as its first arg OS << "void " << kernel_name << "(std::shared_ptr<qcor::CompositeInstruction> parent, " << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << ") {\n"; OS << "class " << kernel_name << " k(parent, " << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ");\n"; OS << "}\n"; // close adjoint() // Declare the previous forward declaration OS << "void __internal_call_function_" << kernel_name << "(" << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << ") {\n"; OS << "class " << kernel_name << " k(" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ");\n"; OS << "}\n"; // close class OS << "};"; auto s = OS.str(); qcor::info("[qcor syntax-handler] Rewriting " + kernel_name + " to\n\n" + Loading @@ -180,10 +258,9 @@ public: } void AddToPredefines(llvm::raw_string_ostream &OS) override { OS << "#include \"qrt.hpp\"\n"; OS << "#include \"qcor.hpp\"\n"; OS << "#include \"xacc_internal_compiler.hpp\"\nusing namespace " "xacc::internal_compiler;\n"; OS << "using namespace xacc::internal_compiler;\n"; } }; Loading Loading @@ -259,7 +336,8 @@ public: // instructions.cbegin(), instructions.cend(), // [](const auto &inst) { return inst->name() != "Measure"; })) { // xacc::error( // "Unable to create Adjoint for kernels that have Measure operations."); // "Unable to create Adjoint for kernels that have Measure // operations."); // } // std::reverse(instructions.begin(), instructions.end()); // for (const auto &inst : instructions) { Loading Loading @@ -297,7 +375,8 @@ public: // // kernelFuncClass(abc).adjoint(); // // -> DTor of the Adjoint instance called here (m_usedAsCallable = true) // // hence adding the adjoint body to the global composite. // // -> DTor of the kernelFuncClass(abc) instance called here (m_usedAsCallable // // -> DTor of the kernelFuncClass(abc) instance called here // (m_usedAsCallable // // = false) hence having no effect. // // ... code ... // virtual ~KernelBase() { Loading @@ -313,9 +392,12 @@ public: // protected: // // Copy ctor: // // Deep copy of the CompositeInstruction to prevent dangling references. // KernelBase(KernelBase *other, const std::string &in_optional_newName = "") { // const auto kernelName = in_optional_newName.empty() ? other->m_body->name() // : in_optional_newName; // KernelBase(KernelBase *other, const std::string &in_optional_newName = "") // { // const auto kernelName = in_optional_newName.empty() ? // other->m_body->name() // : // in_optional_newName; // auto provider = xacc::getIRProvider("quantum"); // m_body = provider->createComposite(kernelName); // for (const auto &inst : other->m_body->getInstructions()) { Loading @@ -332,7 +414,8 @@ public: // // kernelFuncClass(qubitReg).adjoint(); => FALSE (on the original // // kernelFuncClass instance) but TRUE for the one returned by the adjoint() // // member function. This will allow arbitrary chaining: e.g. // // kernelFuncClass(qubitReg).adjoint().ctrl(k); only the last kernel returned // // kernelFuncClass(qubitReg).adjoint().ctrl(k); only the last kernel // returned // // by ctrl() will be the *Callable*; // bool m_usedAsCallable; // // The XACC composite instruction described by this kernel body: Loading Loading @@ -382,7 +465,8 @@ public: // // returning *void* to returing *KernelBase*, // // i.e. we need to be able to rewrite: // // "__qpu__ void" ==> "__qpu__ KernelBase" (__qpu__ is handled by the // // pre-processor) Possibility: the *qcor* script to do that before calling clang // // pre-processor) Possibility: the *qcor* script to do that before calling // clang // // ?? // ////////////////////////////////////////////////// // // TEST kernel-in-kernel Loading lib/hybrid/qcor_hybrid.hpp +2 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ struct TranslationFunctorAutoGenerator { } }; // Utility class to count the number of rotation // angles in a parameterized circuit evaluation class CountRotationAngles { public: std::size_t &count; Loading runtimev2/execution/taskInitiate.hpp +17 −0 Original line number Diff line number Diff line Loading @@ -7,20 +7,33 @@ namespace qcor { // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer, and custom std::function OptFunction delegating // to the ObjectiveFunction. Provide the number of variational parameters Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, std::function<double(const std::vector<double>, std::vector<double> &)> &&opt_function, const int nParameters); // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer OptFunction delegating // to the ObjectiveFunction. Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, qcor::OptFunction &&opt_function); // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer OptFunction delegating // to the ObjectiveFunction. Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, qcor::OptFunction &opt_function); // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer, and custom TranslationFunctor, which will map // vector<double> to the required ObjectiveFunction arguments. Provide the number // variational parameters template <typename... Args> Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, Loading @@ -35,6 +48,10 @@ Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, nParameters); } // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer, and custom TranslationFunctor, which will map // vector<double> to the required ObjectiveFunction arguments. Provide the number // variational parameters and GradientEvaluator template <typename... Args> Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, Loading runtimev2/kernel/quantum_kernel.hpp +46 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,30 @@ namespace qcor { // The QuantumKernel represents the super-class of all qcor // quantum kernel functors. Subclasses of this are auto-generated // via the Clang Syntax Handler capability. Derived classes // provide a destructor implementation that builds up and // submits quantum instructions to the specified backend. This enables // functor-like capability whereby programmers instantiate temporary // instances of this via the constructor call, and the destructor is // immediately called. More advanced usage is of course possible for // qcor developers. // // This class works by taking the Derived type (CRTP) and the kernel function // arguments as template parameters. The Derived type is therefore available for // instantiation within provided static methods on QuantumKernel. The Args... // are stored in a member tuple, and are available for use when evaluating the // kernel. Importantly, QuantumKernel provides static adjoint and ctrl methods // for auto-generating those circuits. // // The Syntax Handler will take kernels like this // __qpu__ void foo(qreg q) { H(q[0]); } // and create a derived type of QuantumKernel like this // class foo : public qcor::QuantumKernel<class foo, qreg> {...}; // 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 { protected: // Tuple holder for variadic kernel arguments Loading @@ -17,6 +41,8 @@ protected: // turn this to false bool is_callable = true; // Turn off destructor execution, useful for // qcor developers, not to be used by clients / programmers bool disable_destructor = false; public: Loading @@ -31,8 +57,10 @@ public: : args_tuple(std::make_tuple(args...)), parent_kernel(_parent_kernel), is_callable(false) {} // Nullary constructor, should not be callable QuantumKernel() : is_callable(false) {} // Create the Adjoint of this quantum kernel static void adjoint(std::shared_ptr<CompositeInstruction> parent_kernel, Args... args) { Loading Loading @@ -70,6 +98,24 @@ public: // no measures, so no execute } // Create the controlled version of this quantum kernel static void ctrl(std::shared_ptr<CompositeInstruction> parent_kernel, size_t ctrlIdx, Args... args) { // instantiate and don't let it call the destructor Derived derived; derived.disable_destructor = true; // run the operator()(args...) call to get the parent_kernel derived(args...); // get the instructions auto instructions = derived.parent_kernel->getInstructions(); // Use the controlled gate module of XACC to transform // controlledKernel.m_body // auto ctrlKernel = quantum::controlledKernel(derived.parent_kernel, // ctrlIdx); } virtual ~QuantumKernel() {} }; } // namespace qcor No newline at end of file runtimev2/objectives/objective_function.hpp +27 −12 Original line number Diff line number Diff line Loading @@ -17,13 +17,13 @@ std::shared_ptr<ObjectiveFunction> get_objective(const std::string &type); // The ObjectiveFunction represents a functor-like data structure that // models a general parameterized scalar function. It is initialized with a // problem-specific Observable and Quantum Kernel, and exposes a method for // evaluation, given a list or array of scalar parameters. // evaluation, given a sequence of general function arguments. // Implementations of this concept are problem-specific, and leverage the // observe() functionality of the provided Observable to produce one or many // measured Kernels that are then queued for execution on the available quantum // co-processor, given the current value of the input parameters. The results of // co-processor, given the current value of the input arguments. The results of // these quantum executions are to be used by the ObjectiveFunction to return a // list of scalar values, representing the evaluation of the ObjectiveFunction // scalar value (double), representing the evaluation of the ObjectiveFunction // at the given set of input parameters. Furthermore, the ObjectiveFunction has // access to a global ResultBuffer that it uses to publish execution results at // the current input parameters Loading @@ -43,9 +43,17 @@ protected: // The buffer containing all execution results xacc::internal_compiler::qreg qreg; // Bool to indicate if this ObjectiveFunction // was initialized with a CompositeInstruction // and not a functor bool kernel_is_xacc_composite = false; // Reference to any extra options for objective function // execution HeterogeneousMap options; // Reference to the current iteration's parameters std::vector<double> current_iterate_parameters; // To be implemented by subclasses. Subclasses Loading Loading @@ -79,6 +87,7 @@ public: pointer_to_functor = qk; } // Set any extra options needed for the objective function void set_options(HeterogeneousMap &opts) { options = opts; } // Set the results buffer Loading @@ -105,33 +114,41 @@ public: if (kernel_is_xacc_composite) { kernel->updateRuntimeArguments(args...); } else { // create a temporary // create a temporary with name given by mem_location_qkernel std::stringstream name_ss; name_ss << this << "_qkernel"; kernel = qcor::__internal__::create_composite(name_ss.str()); // Run the functor, this will add // all quantum instructions to the parent kernel functor(kernel, args...); // Set the current iteration parameters current_iterate_parameters.clear(); __internal__::ConvertDoubleLikeToVectorDouble convert( current_iterate_parameters); __internal__::tuple_for_each(std::make_tuple(args...), convert); } // Run the subclass operator()() method return operator()(); } }; // Create an ObjectiveFunction of given name, with a CompositeInstruction // and a shared_ptr to an Observable. std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, std::shared_ptr<CompositeInstruction> kernel, std::shared_ptr<Observable> observable, HeterogeneousMap &&options = {}); // Create an ObjectiveFunction of given name, with a CompositeInstruction // and a reference to an Observable. std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, std::shared_ptr<CompositeInstruction> kernel, Observable &observable, HeterogeneousMap &&options = {}); // Create an Objective Function that makes calls to the // provided Quantum Kernel, with measurements dictated by // the provided Observable. Optionally can provide problem-specific // options map. // Create an ObjectiveFunction of given name, with a quantum kernel functor // and a shared_ptr to an Observable. template <typename... Args> std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, Loading @@ -147,10 +164,8 @@ std::shared_ptr<ObjectiveFunction> createObjectiveFunction( return obj_func; } // This method takes as input a functor with a signature // that takes a CompositeInstruction as the first input argument, // followed by other arguments that feed into the creation of the // quantum kernel. // Create an ObjectiveFunction of given name, with a quantum kernel functor // and a reference to an Observable. template <typename... Args> std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, Loading Loading
handlers/qcor_syntax_handler.cpp +151 −67 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ namespace { bool qrt = false; std::string qpu_name = "qpp"; int shots = 0; int shots = 1024; class QCORSyntaxHandler : public SyntaxHandler { public: Loading @@ -24,8 +24,6 @@ public: void GetReplacement(Preprocessor &PP, Declarator &D, CachedTokens &Toks, llvm::raw_string_ostream &OS) override { // FIXME need way to get backend name from user/command line // Get the Diagnostics engine and create a few custom // error messgaes auto &diagnostics = PP.getDiagnostics(); Loading Loading @@ -89,70 +87,121 @@ public: auto new_src = qcor::run_token_collector(PP, Toks, bufferNames); OS << function_prototype << "{\n"; OS << "quantum::initialize(\"" << qpu_name << "\", \"" << kernel_name << "\");\n"; for (auto &buf : bufferNames) { OS << buf << ".setNameAndStore(\"" + buf + "\");\n"; // First re-write, forward declare a function // we will implement further down OS << "void __internal_call_function_" << kernel_name << "(" << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << ");\n"; if (shots > 0) { OS << "quantum::set_shots(" << shots << ");\n"; // Call that forward declared function with the function args OS << "__internal_call_function_" << kernel_name << "(" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << new_src; OS << "if (__execute) {\n"; OS << ");\n"; if (bufferNames.size() > 1) { OS << "xacc::AcceleratorBuffer * buffers[" << bufferNames.size() << "] = {"; OS << bufferNames[0] << ".results()"; for (unsigned int k = 1; k < bufferNames.size(); k++) { OS << ", " << bufferNames[k] << ".results()"; } OS << "};\n"; OS << "std::cout << \"execing: \" << quantum::getProgram()->toString() " "<< \"\\n\";\n"; // Close the transformed function OS << "}\n"; OS << "quantum::submit(buffers," << bufferNames.size(); } else { OS << "quantum::submit(" << bufferNames[0] << ".results()"; // Declare the QuantumKernel subclass OS << "class " << kernel_name << " : public qcor::QuantumKernel<class " << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << "> {\n"; OS << ");\n"; OS << "}"; OS << "\n}\n"; OS << "class " << kernel_name << "{\n"; OS << "public:\n"; OS << "static void adjoint("; for (int i = 0; i < program_arg_types.size(); i++) { if (i > 0) { OS << ","; // declare the super-type as a friend OS << "friend class qcor::QuantumKernel<class " << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } auto arg_type = program_arg_types[i]; auto arg_var = program_parameters[i]; OS << arg_type << " " << arg_var; OS << ">;\n"; // declare protected operator()() method OS << "protected:\n"; OS << "void operator()(" << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << ") {\n"; OS << "if (!parent_kernel) {\n"; OS << "parent_kernel = " "qcor::__internal__::create_composite(kernel_name);\n"; OS << "// q.setNameAndStore();\n"; OS << "}\n"; OS << "quantum::set_current_program(parent_kernel);\n"; OS << new_src << "\n"; OS << "}\n"; OS << "quantum::initialize(\"" << qpu_name << "\", \"" << kernel_name << "\");\n"; for (auto &buf : bufferNames) { OS << buf << ".setNameAndStore(\"" + buf + "\");\n"; // declare public members, methods, constructors OS << "public:\n"; OS << "inline static const std::string kernel_name = \"" << kernel_name << "\";\n"; // First constructor, default one KERNEL_NAME(Args...); OS << kernel_name << "(" << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } if (shots > 0) { OS << "quantum::set_shots(" << shots << ");\n"; OS << "): QuantumKernel<" << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << "> (" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ") {}\n"; // Second constructor, takes parent CompositeInstruction // KERNEL_NAME(CompositeInstruction, Args...) OS << kernel_name << "(std::shared_ptr<qcor::CompositeInstruction> _parent, " << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << "): QuantumKernel<" << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << "> (_parent, " << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ") {}\n"; OS << new_src; OS << "quantum::adjoint();\n"; OS << "if (__execute) {\n"; // Third constructor, nullary constructor OS << kernel_name << "() : QuantumKernel<" << kernel_name << ", " << program_arg_types[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i]; } OS << ">() {}\n"; // Destructor definition OS << "virtual ~" << kernel_name << "() {\n"; OS << "if (disable_destructor) {return;}\n"; OS << "quantum::set_backend(\"" << qpu_name << "\", " << shots << ");\n"; OS << "auto [" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << "] = args_tuple;\n"; OS << "operator()(" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ");\n"; OS << "if (is_callable) {\n"; if (bufferNames.size() > 1) { OS << "xacc::AcceleratorBuffer * buffers[" << bufferNames.size() << "] = {"; Loading @@ -166,13 +215,42 @@ public: OS << "quantum::submit(" << bufferNames[0] << ".results()"; } OS << ");\n"; OS << "}\n"; OS << "}\n"; // close the quantum kernel subclass OS << "};\n"; // Add a function with the kernel_name that takes // a parent CompositeInstruction as its first arg OS << "void " << kernel_name << "(std::shared_ptr<qcor::CompositeInstruction> parent, " << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << ") {\n"; OS << "class " << kernel_name << " k(parent, " << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ");\n"; OS << "}\n"; // close adjoint() // Declare the previous forward declaration OS << "void __internal_call_function_" << kernel_name << "(" << program_arg_types[0] << " " << program_parameters[0]; for (int i = 1; i < program_arg_types.size(); i++) { OS << ", " << program_arg_types[i] << " " << program_parameters[i]; } OS << ") {\n"; OS << "class " << kernel_name << " k(" << program_parameters[0]; for (int i = 1; i < program_parameters.size(); i++) { OS << ", " << program_parameters[i]; } OS << ");\n"; OS << "}\n"; // close class OS << "};"; auto s = OS.str(); qcor::info("[qcor syntax-handler] Rewriting " + kernel_name + " to\n\n" + Loading @@ -180,10 +258,9 @@ public: } void AddToPredefines(llvm::raw_string_ostream &OS) override { OS << "#include \"qrt.hpp\"\n"; OS << "#include \"qcor.hpp\"\n"; OS << "#include \"xacc_internal_compiler.hpp\"\nusing namespace " "xacc::internal_compiler;\n"; OS << "using namespace xacc::internal_compiler;\n"; } }; Loading Loading @@ -259,7 +336,8 @@ public: // instructions.cbegin(), instructions.cend(), // [](const auto &inst) { return inst->name() != "Measure"; })) { // xacc::error( // "Unable to create Adjoint for kernels that have Measure operations."); // "Unable to create Adjoint for kernels that have Measure // operations."); // } // std::reverse(instructions.begin(), instructions.end()); // for (const auto &inst : instructions) { Loading Loading @@ -297,7 +375,8 @@ public: // // kernelFuncClass(abc).adjoint(); // // -> DTor of the Adjoint instance called here (m_usedAsCallable = true) // // hence adding the adjoint body to the global composite. // // -> DTor of the kernelFuncClass(abc) instance called here (m_usedAsCallable // // -> DTor of the kernelFuncClass(abc) instance called here // (m_usedAsCallable // // = false) hence having no effect. // // ... code ... // virtual ~KernelBase() { Loading @@ -313,9 +392,12 @@ public: // protected: // // Copy ctor: // // Deep copy of the CompositeInstruction to prevent dangling references. // KernelBase(KernelBase *other, const std::string &in_optional_newName = "") { // const auto kernelName = in_optional_newName.empty() ? other->m_body->name() // : in_optional_newName; // KernelBase(KernelBase *other, const std::string &in_optional_newName = "") // { // const auto kernelName = in_optional_newName.empty() ? // other->m_body->name() // : // in_optional_newName; // auto provider = xacc::getIRProvider("quantum"); // m_body = provider->createComposite(kernelName); // for (const auto &inst : other->m_body->getInstructions()) { Loading @@ -332,7 +414,8 @@ public: // // kernelFuncClass(qubitReg).adjoint(); => FALSE (on the original // // kernelFuncClass instance) but TRUE for the one returned by the adjoint() // // member function. This will allow arbitrary chaining: e.g. // // kernelFuncClass(qubitReg).adjoint().ctrl(k); only the last kernel returned // // kernelFuncClass(qubitReg).adjoint().ctrl(k); only the last kernel // returned // // by ctrl() will be the *Callable*; // bool m_usedAsCallable; // // The XACC composite instruction described by this kernel body: Loading Loading @@ -382,7 +465,8 @@ public: // // returning *void* to returing *KernelBase*, // // i.e. we need to be able to rewrite: // // "__qpu__ void" ==> "__qpu__ KernelBase" (__qpu__ is handled by the // // pre-processor) Possibility: the *qcor* script to do that before calling clang // // pre-processor) Possibility: the *qcor* script to do that before calling // clang // // ?? // ////////////////////////////////////////////////// // // TEST kernel-in-kernel Loading
lib/hybrid/qcor_hybrid.hpp +2 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ struct TranslationFunctorAutoGenerator { } }; // Utility class to count the number of rotation // angles in a parameterized circuit evaluation class CountRotationAngles { public: std::size_t &count; Loading
runtimev2/execution/taskInitiate.hpp +17 −0 Original line number Diff line number Diff line Loading @@ -7,20 +7,33 @@ namespace qcor { // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer, and custom std::function OptFunction delegating // to the ObjectiveFunction. Provide the number of variational parameters Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, std::function<double(const std::vector<double>, std::vector<double> &)> &&opt_function, const int nParameters); // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer OptFunction delegating // to the ObjectiveFunction. Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, qcor::OptFunction &&opt_function); // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer OptFunction delegating // to the ObjectiveFunction. Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, qcor::OptFunction &opt_function); // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer, and custom TranslationFunctor, which will map // vector<double> to the required ObjectiveFunction arguments. Provide the number // variational parameters template <typename... Args> Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, Loading @@ -35,6 +48,10 @@ Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, nParameters); } // Execute asynchronous task - find optimal value of the given objective function // using the provided optimizer, and custom TranslationFunctor, which will map // vector<double> to the required ObjectiveFunction arguments. Provide the number // variational parameters and GradientEvaluator template <typename... Args> Handle taskInitiate(std::shared_ptr<ObjectiveFunction> objective, std::shared_ptr<Optimizer> optimizer, Loading
runtimev2/kernel/quantum_kernel.hpp +46 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,30 @@ namespace qcor { // The QuantumKernel represents the super-class of all qcor // quantum kernel functors. Subclasses of this are auto-generated // via the Clang Syntax Handler capability. Derived classes // provide a destructor implementation that builds up and // submits quantum instructions to the specified backend. This enables // functor-like capability whereby programmers instantiate temporary // instances of this via the constructor call, and the destructor is // immediately called. More advanced usage is of course possible for // qcor developers. // // This class works by taking the Derived type (CRTP) and the kernel function // arguments as template parameters. The Derived type is therefore available for // instantiation within provided static methods on QuantumKernel. The Args... // are stored in a member tuple, and are available for use when evaluating the // kernel. Importantly, QuantumKernel provides static adjoint and ctrl methods // for auto-generating those circuits. // // The Syntax Handler will take kernels like this // __qpu__ void foo(qreg q) { H(q[0]); } // and create a derived type of QuantumKernel like this // class foo : public qcor::QuantumKernel<class foo, qreg> {...}; // 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 { protected: // Tuple holder for variadic kernel arguments Loading @@ -17,6 +41,8 @@ protected: // turn this to false bool is_callable = true; // Turn off destructor execution, useful for // qcor developers, not to be used by clients / programmers bool disable_destructor = false; public: Loading @@ -31,8 +57,10 @@ public: : args_tuple(std::make_tuple(args...)), parent_kernel(_parent_kernel), is_callable(false) {} // Nullary constructor, should not be callable QuantumKernel() : is_callable(false) {} // Create the Adjoint of this quantum kernel static void adjoint(std::shared_ptr<CompositeInstruction> parent_kernel, Args... args) { Loading Loading @@ -70,6 +98,24 @@ public: // no measures, so no execute } // Create the controlled version of this quantum kernel static void ctrl(std::shared_ptr<CompositeInstruction> parent_kernel, size_t ctrlIdx, Args... args) { // instantiate and don't let it call the destructor Derived derived; derived.disable_destructor = true; // run the operator()(args...) call to get the parent_kernel derived(args...); // get the instructions auto instructions = derived.parent_kernel->getInstructions(); // Use the controlled gate module of XACC to transform // controlledKernel.m_body // auto ctrlKernel = quantum::controlledKernel(derived.parent_kernel, // ctrlIdx); } virtual ~QuantumKernel() {} }; } // namespace qcor No newline at end of file
runtimev2/objectives/objective_function.hpp +27 −12 Original line number Diff line number Diff line Loading @@ -17,13 +17,13 @@ std::shared_ptr<ObjectiveFunction> get_objective(const std::string &type); // The ObjectiveFunction represents a functor-like data structure that // models a general parameterized scalar function. It is initialized with a // problem-specific Observable and Quantum Kernel, and exposes a method for // evaluation, given a list or array of scalar parameters. // evaluation, given a sequence of general function arguments. // Implementations of this concept are problem-specific, and leverage the // observe() functionality of the provided Observable to produce one or many // measured Kernels that are then queued for execution on the available quantum // co-processor, given the current value of the input parameters. The results of // co-processor, given the current value of the input arguments. The results of // these quantum executions are to be used by the ObjectiveFunction to return a // list of scalar values, representing the evaluation of the ObjectiveFunction // scalar value (double), representing the evaluation of the ObjectiveFunction // at the given set of input parameters. Furthermore, the ObjectiveFunction has // access to a global ResultBuffer that it uses to publish execution results at // the current input parameters Loading @@ -43,9 +43,17 @@ protected: // The buffer containing all execution results xacc::internal_compiler::qreg qreg; // Bool to indicate if this ObjectiveFunction // was initialized with a CompositeInstruction // and not a functor bool kernel_is_xacc_composite = false; // Reference to any extra options for objective function // execution HeterogeneousMap options; // Reference to the current iteration's parameters std::vector<double> current_iterate_parameters; // To be implemented by subclasses. Subclasses Loading Loading @@ -79,6 +87,7 @@ public: pointer_to_functor = qk; } // Set any extra options needed for the objective function void set_options(HeterogeneousMap &opts) { options = opts; } // Set the results buffer Loading @@ -105,33 +114,41 @@ public: if (kernel_is_xacc_composite) { kernel->updateRuntimeArguments(args...); } else { // create a temporary // create a temporary with name given by mem_location_qkernel std::stringstream name_ss; name_ss << this << "_qkernel"; kernel = qcor::__internal__::create_composite(name_ss.str()); // Run the functor, this will add // all quantum instructions to the parent kernel functor(kernel, args...); // Set the current iteration parameters current_iterate_parameters.clear(); __internal__::ConvertDoubleLikeToVectorDouble convert( current_iterate_parameters); __internal__::tuple_for_each(std::make_tuple(args...), convert); } // Run the subclass operator()() method return operator()(); } }; // Create an ObjectiveFunction of given name, with a CompositeInstruction // and a shared_ptr to an Observable. std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, std::shared_ptr<CompositeInstruction> kernel, std::shared_ptr<Observable> observable, HeterogeneousMap &&options = {}); // Create an ObjectiveFunction of given name, with a CompositeInstruction // and a reference to an Observable. std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, std::shared_ptr<CompositeInstruction> kernel, Observable &observable, HeterogeneousMap &&options = {}); // Create an Objective Function that makes calls to the // provided Quantum Kernel, with measurements dictated by // the provided Observable. Optionally can provide problem-specific // options map. // Create an ObjectiveFunction of given name, with a quantum kernel functor // and a shared_ptr to an Observable. template <typename... Args> std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, Loading @@ -147,10 +164,8 @@ std::shared_ptr<ObjectiveFunction> createObjectiveFunction( return obj_func; } // This method takes as input a functor with a signature // that takes a CompositeInstruction as the first input argument, // followed by other arguments that feed into the creation of the // quantum kernel. // Create an ObjectiveFunction of given name, with a quantum kernel functor // and a reference to an Observable. template <typename... Args> std::shared_ptr<ObjectiveFunction> createObjectiveFunction( const std::string &obj_name, Loading