/******************************************************************************* * Copyright (c) 2018 UT-Battelle, LLC. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompanies this * distribution. The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution *License is available at https://eclipse.org/org/documents/edl-v10.php * * Contributors: * Alexander J. McCaskey - initial API and implementation *******************************************************************************/ #include "XACC.hpp" #include "IRGenerator.hpp" #include "IRProvider.hpp" #include "InstructionIterator.hpp" #include "AcceleratorBuffer.hpp" #include "AcceleratorDecorator.hpp" #include "InstructionParameter.hpp" #include "DWGraph.hpp" #include "EmbeddingAlgorithm.hpp" #include "PauliOperator.hpp" #include #include #include #include #include #include #include namespace py = pybind11; using namespace xacc; using namespace xacc::quantum; // `boost::variant` as an example -- can be any `std::variant`-like container namespace pybind11 { namespace detail { template struct type_caster> : variant_caster> {}; // Specifies the function used to visit the variant -- `apply_visitor` instead // of `visit` template <> struct visit_helper { template static auto call(Args &&... args) -> decltype(mpark::visit(args...)) { return mpark::visit(args...); } }; template struct type_caster> : variant_caster> {}; // Specifies the function used to visit the variant -- `apply_visitor` instead // of `visit` template <> struct visit_helper { template static auto call(Args &&... args) -> decltype(mpark::visit(args...)) { return mpark::visit(args...); } }; } // namespace detail } // namespace pybind11 class PyAccelerator : public xacc::Accelerator { public: /* Inherit the constructors */ using Accelerator::Accelerator; const std::string name() const override { PYBIND11_OVERLOAD_PURE(const std::string, xacc::Accelerator, name); } const std::string description() const override { return ""; } void initialize() override { return; } AcceleratorType getType() override { return Accelerator::AcceleratorType::qpu_gate; } std::vector> getIRTransformations() override { return {}; } bool isValidBufferSize(const int n) override { return true; } /* Trampoline (need one for each virtual function) */ void execute(std::shared_ptr buf, std::shared_ptr f) override { PYBIND11_OVERLOAD_PURE(void, xacc::Accelerator, execute, buf, f); } std::vector> execute(std::shared_ptr buffer, const std::vector> functions) override { return {}; } std::shared_ptr createBuffer(const std::string &varId) override { return std::make_shared(varId, 100); } std::shared_ptr createBuffer(const std::string &varId, const int size) override { return std::make_shared(varId, size); } }; PYBIND11_MODULE(_pyxacc, m) { m.doc() = "Python bindings for XACC. XACC provides a plugin infrastructure for " "programming, compiling, and executing quantum kernels in a language and " "hardware agnostic manner."; py::class_( m, "InstructionParameter", "The InstructionParameter provides a variant structure " "to provide parameters to XACC Instructions at runtime. " "This type can be an int, double, float, string, or complex value.") .def(py::init(), "Construct as an int.") .def(py::init(), "Construct as a double.") .def(py::init(), "Construct as a string.") .def(py::init>(), "Construct as a complex double."); py::class_>( m, "Instruction", "") .def("nParameters", &xacc::Instruction::nParameters, "") .def("toString", (const std::string (xacc::Instruction::*)()) & xacc::Instruction::toString, "") .def("toString", (const std::string (xacc::Instruction::*)(const std::string &)) & xacc::Instruction::toString, "") .def("isEnabled", &xacc::Instruction::isEnabled, "") .def("isComposite", &xacc::Instruction::isComposite, "") .def("bits", &xacc::Instruction::bits, "") .def("getParameter", &xacc::Instruction::getParameter, "") .def("getParameters", &xacc::Instruction::getParameters, "") .def("setParameter", &xacc::Instruction::setParameter, "") .def("mapBits", &xacc::Instruction::mapBits, "") .def("name", &xacc::Instruction::name, "") .def("description", &xacc::Instruction::description, ""); py::class_>(m, "Function", "") .def("nInstructions", &xacc::Function::nInstructions, "") .def("getInstruction", &xacc::Function::getInstruction, "") .def("getInstructions", &xacc::Function::getInstructions, "") .def("removeInstruction", &xacc::Function::removeInstruction, "") .def("replaceInstruction", &xacc::Function::replaceInstruction, "") .def("insertInstruction", &xacc::Function::insertInstruction, "") .def("addInstruction", &xacc::Function::addInstruction, "") .def("addParameter", &xacc::Function::addParameter, "") .def("nParameters", &xacc::Function::nParameters, "") .def("eval", &xacc::Function::operator(), "") .def("name", &xacc::Function::name, "") .def("description", &xacc::Function::description, "") .def("nParameters", &xacc::Function::nParameters, "") .def("toString", (const std::string (xacc::Function::*)()) & xacc::Function::toString, "") .def("toString", (const std::string (xacc::Function::*)(const std::string &)) & xacc::Function::toString, "") .def("enabledView", &xacc::Function::enabledView, "") .def("enable", &xacc::Function::enable, "") .def("getParameter", &xacc::Function::getParameter, "") .def("getParameters", &xacc::Function::getParameters, "") .def("setParameter", &xacc::Function::setParameter, "") .def("depth", &xacc::Function::depth, "") .def("persistGraph", &xacc::Function::persistGraph, "") .def("mapBits", &xacc::Function::mapBits, ""); // Expose the IR interface py::class_>( m, "IR", "The XACC Intermediate Representation, " "serves as a container for XACC Functions.") .def("getKernels", &xacc::IR::getKernels, "Return the kernels in this IR") .def("addKernel", &xacc::IR::addKernel, ""); py::class_(m, "InstructionIterator", "") .def(py::init>()) .def("hasNext", &xacc::InstructionIterator::hasNext, "") .def("next", &xacc::InstructionIterator::next, ""); py::class_>( m, "IRPreprocesor", "") .def("process", &xacc::IRPreprocessor::process, ""); py::class_>( m, "IRTransformation", "") .def("transform", &xacc::IRTransformation::transform, ""); py::class_>( m, "AcceleratorBufferPostprocessor", "") .def("process", &xacc::AcceleratorBufferPostprocessor::process, ""); py::class_>( m, "IRGenerator", "") .def("generate", (std::shared_ptr(xacc::IRGenerator::*)( std::vector)) & xacc::IRGenerator::generate, py::return_value_policy::reference, "") .def("generate", (std::shared_ptr(xacc::IRGenerator::*)( std::map)) & xacc::IRGenerator::generate, py::return_value_policy::reference, "") .def("analyzeResults", &xacc::IRGenerator::analyzeResults, ""); // Expose the Kernel py::class_, std::shared_ptr>>( m, "Kernel", "The XACC Kernel is the " "executable functor that executes XACC IR on the desired Accelerator.") .def("getIRFunction", &xacc::Kernel<>::getIRFunction, py::return_value_policy::reference, "Return the IR Function instance this Kernel wraps.") .def("execute", (void (xacc::Kernel<>::*)(std::shared_ptr, std::vector)) & xacc::Kernel<>::operator(), "Execute this Kernel with the given set of " "InstructionParamters. This set can be empty " "if there are no parameters."); py::class_>( m, "KernelList", "The XACC KernelList is a vector of " "Kernels that provides a operator() implementation to execute multiple " "kernels at once.") .def( "execute", (std::vector>( xacc::KernelList<>::*)(std::shared_ptr, std::vector)) & xacc::KernelList<>::operator(), "Execute a list of Kernels at once.") .def("__getitem__", [](const xacc::KernelList<> &kl, int i) -> xacc::Kernel<> { if (i >= kl.size()) throw py::index_error(); return kl[i]; }) .def("__setitem__", [](xacc::KernelList<> &kl, size_t i, xacc::Kernel<> v) { if (i >= kl.size()) throw py::index_error(); kl[i] = v; }) .def("__len__", &xacc::KernelList<>::size) .def( "__iter__", [](const xacc::KernelList<> &kl) { return py::make_iterator(kl.begin(), kl.end()); }, py::keep_alive<0, 1>()) .def("__getitem__", [](xacc::KernelList<> &s, py::slice slice) -> xacc::KernelList<> * { size_t start, stop, step, slicelength; if (!slice.compute(s.size(), &start, &stop, &step, &slicelength)) throw py::error_already_set(); xacc::KernelList<> *seq = new xacc::KernelList<>(s.getAccelerator()); for (size_t i = 0; i < slicelength; ++i) { (*seq).push_back(s[start]); start += step; } return seq; }); // Expose the Accelerator py::class_, PyAccelerator> acc(m, "Accelerator", "Accelerator wraps the XACC C++ Accelerator class " "and provides a mechanism for creating buffers of qubits. Execution " "is handled by the XACC Kernels."); acc.def(py::init<>()) .def("name", &xacc::Accelerator::name, "Return the name of this Accelerator.") .def("createBuffer", (std::shared_ptr(xacc::Accelerator::*)( const std::string &, const int)) & xacc::Accelerator::createBuffer, py::return_value_policy::reference, "Return a newly created register of qubits of the given size.") .def( "createBuffer", (std::shared_ptr(xacc::Accelerator::*)( const std::string &)) & xacc::Accelerator::createBuffer, py::return_value_policy::reference, "Return a newly allocated register of all qubits on the Accelerator.") .def("execute", (void (xacc::Accelerator::*)(std::shared_ptr, const std::shared_ptr)) & xacc::Accelerator::execute, "Execute the Function with the given AcceleratorBuffer.") .def("execute", (std::vector>( xacc::Accelerator::*)( std::shared_ptr, const std::vector>)) & xacc::Accelerator::execute, "Execute the Function with the given AcceleratorBuffer.") .def("initialize", &xacc::Accelerator::initialize, "") .def("getOneBitErrorRates", &xacc::Accelerator::getOneBitErrorRates, "") .def("getTwoBitErrorRates", &xacc::Accelerator::getTwoBitErrorRates, ""); py::enum_(acc, "AcceleratorType") .value("qpu_aqc", Accelerator::AcceleratorType::qpu_aqc) .value("qpu_gate", Accelerator::AcceleratorType::qpu_gate) .export_values(); py::class_> accd(m, "AcceleratorDecorator", ""); accd.def("setDecorated", &xacc::AcceleratorDecorator::setDecorated, ""); // Expose the AcceleratorBuffer py::class_>( m, "AcceleratorBuffer", "The AcceleratorBuffer models a register of qubits.") .def(py::init()) .def("getExpectationValueZ", &xacc::AcceleratorBuffer::getExpectationValueZ, "Return the expectation value with respect to the Z operator.") .def("resetBuffer", &xacc::AcceleratorBuffer::resetBuffer, "Reset this buffer for use in another computation.") .def("size", &xacc::AcceleratorBuffer::size, "") .def("appendMeasurement", (void (xacc::AcceleratorBuffer::*)(const std::string &)) & xacc::AcceleratorBuffer::appendMeasurement, "Append the measurement string") .def("getMeasurements", &xacc::AcceleratorBuffer::getMeasurements, "Return observed measurement bit strings") .def("computeMeasurementProbability", &xacc::AcceleratorBuffer::computeMeasurementProbability, "Compute the probability of a given bit string") .def("getMeasurementCounts", &xacc::AcceleratorBuffer::getMeasurementCounts, "Return the mapping of measure bit strings to their counts.") .def("getChildren", (std::vector>( xacc::AcceleratorBuffer::*)(const std::string)) & xacc::AcceleratorBuffer::getChildren, "") .def("nChildren", &xacc::AcceleratorBuffer::nChildren, "") .def("getChildren", (std::vector>( xacc::AcceleratorBuffer::*)()) & xacc::AcceleratorBuffer::getChildren, "") .def("getChildrenNames", &xacc::AcceleratorBuffer::getChildrenNames, "") .def("listExtraInfoKeys", &xacc::AcceleratorBuffer::listExtraInfoKeys, "") .def("getInformation", (std::map(xacc::AcceleratorBuffer::*)()) & xacc::AcceleratorBuffer::getInformation, "") .def("addExtraInfo", (void (xacc::AcceleratorBuffer::*)(const std::string, ExtraInfo)) & xacc::AcceleratorBuffer::addExtraInfo, "") .def("getInformation", (ExtraInfo(xacc::AcceleratorBuffer::*)(const std::string)) & xacc::AcceleratorBuffer::getInformation, "") .def("appendChild", &xacc::AcceleratorBuffer::appendChild, "") .def("hasExtraInfoKey", &xacc::AcceleratorBuffer::hasExtraInfoKey, "") .def("name", &xacc::AcceleratorBuffer::name, "") .def("getAllUnique", &xacc::AcceleratorBuffer::getAllUnique, "Return all unique information with the provided string name") .def("__repr__", &xacc::AcceleratorBuffer::toString, "") .def("__str__", &xacc::AcceleratorBuffer::toString, "") .def("getChildren", (std::vector>( xacc::AcceleratorBuffer::*)(const std::string, ExtraInfo)) & xacc::AcceleratorBuffer::getChildren, ""); // Expose the Compiler py::class_>( m, "Compiler", "The XACC Compiler takes as input quantum kernel source code, " "and compiles it to the XACC intermediate representation.") .def("name", &xacc::Compiler::name, "Return the name of this Compiler.") .def("compile", (std::shared_ptr(xacc::Compiler::*)( const std::string &, std::shared_ptr)) & xacc::Compiler::compile, "Compile the " "given source code against the given Accelerator.") .def("compile", (std::shared_ptr(xacc::Compiler::*)(const std::string &)) & xacc::Compiler::compile, "Compile the " "given source code.") .def("translate", &xacc::Compiler::translate, "Translate the given IR Function instance to source code in this " "Compiler's language."); py::class_(m, "Term").def("coeff", &Term::coeff).def("ops",&Term::ops); py::class_(m, "PauliOperator") .def(py::init<>()) .def(py::init>()) .def(py::init()) .def(py::init()) .def(py::init>()) .def(py::init, double>()) .def(py::init, std::complex>()) .def(py::init, std::string>()) .def(py::init, std::complex, std::string>()) .def(py::self + py::self) .def(py::self += py::self) .def(py::self *= py::self) .def(py::self *= double()) .def(py::self * py::self) .def(py::self *= std::complex()) .def(py::self -= py::self) .def(py::self - py::self) .def("__eq__", &PauliOperator::operator==) .def("__repr__", &PauliOperator::toString) .def("eval", &PauliOperator::eval) .def("toBinaryVectors", &PauliOperator::toBinaryVectors) .def("toXACCIR", &PauliOperator::toXACCIR) .def("fromXACCIR", &PauliOperator::fromXACCIR) .def("fromString", &PauliOperator::fromString) .def("nTerms", &PauliOperator::nTerms) .def("isClose", &PauliOperator::isClose) .def("commutes", &PauliOperator::commutes) .def("__len__", &PauliOperator::nTerms) .def("nQubits", &PauliOperator::nQubits) .def("computeActionOnKet", &PauliOperator::computeActionOnKet) .def("computeActionOnBra", &PauliOperator::computeActionOnBra) .def("__iter__", [](PauliOperator &op) { return py::make_iterator(op.begin(), op.end()); }, py::keep_alive<0, 1>()); // Expose the Program object py::class_( m, "Program", "The Program is the primary entrypoint for compilation and execution in " "XACC. Clients provide quantum kernel source " "code and the Accelerator instance, and the Program handles compiling " "the code and provides Kernel instances to execute.") .def(py::init, const std::string &>(), "The constructor") .def(py::init, std::shared_ptr>(), "The constructor") .def("build", (void (xacc::Program::*)(const std::string &)) & xacc::Program::build, "Compile this program with the given Compiler name.") .def("build", (void (xacc::Program::*)()) & xacc::Program::build, "Compile this program.") .def("getKernel", (xacc::Kernel<>(xacc::Program::*)(const std::string &)) & xacc::Program::getKernel<>, py::return_value_policy::reference, "Return a Kernel representing the source code.") .def("getKernels", &xacc::Program::getRuntimeKernels, "Return all Kernels.") .def("nKernels", &xacc::Program::nKernels, "Return the number of kernels compiled by this program"); // Expose XACC API functions m.def("Initialize", (void (*)(std::vector)) & xacc::Initialize, "Initialize the framework. Can provide a list of strings to model " "command line arguments. For instance, " "to print the XACC help message, pass ['--help'] to this function, or " "to set the compiler to use, ['--compiler','scaffold']."); m.def("Initialize", (void (*)()) & xacc::Initialize, "Initialize the framework. Use this if there are no command line " "arguments to pass."); m.def("PyInitialize", &xacc::PyInitialize, "Initialize the framework from Python."); // m.def("help", ) m.def("getAccelerator", (std::shared_ptr(*)(const std::string &)) & xacc::getAccelerator, py::return_value_policy::reference, "Return the accelerator with given name."); m.def("getCompiler", (std::shared_ptr(*)(const std::string &)) & xacc::getCompiler, py::return_value_policy::reference, "Return the Compiler of given name."); m.def("getIRPreprocessor", (std::shared_ptr(*)(const std::string &)) & xacc::getService, py::return_value_policy::reference, "Return the IRPreprocessor of given name."); m.def("getIRTransformation", (std::shared_ptr(*)(const std::string &)) & xacc::getService, py::return_value_policy::reference, "Return the IRTransformation of given name."); m.def("getIRGenerator", (std::shared_ptr(*)(const std::string &)) & xacc::getService, py::return_value_policy::reference, "Return the IRGenerator of given name."); m.def("getConnectivity", [](const std::string acc) -> std::vector> { auto a = xacc::getAccelerator(acc); return a->getAcceleratorConnectivity(); }); m.def("translate", &xacc::translate, "Translate the provided IR Function to the given language."); m.def("setOption", [](const std::string s, InstructionParameter p) { xacc::setOption(s, p.toString()); }); m.def( "info", [](const std::string s) { xacc::info(s); }, ""); m.def( "error", [](const std::string s) { xacc::error(s); }, ""); m.def("setOption", &xacc::setOption, "Set an XACC framework option."); m.def( "setOptions", [](std::map options) { for (auto &kv : options) xacc::setOption(kv.first, kv.second); }, "Set a number of options at once."); m.def("getAcceleratorDecorator", [](const std::string name, std::shared_ptr acc) { auto accd = xacc::getService(name); accd->setDecorated(acc); return accd; }); m.def( "setOptions", [](std::map options) { for (auto &kv : options) xacc::setOption(kv.first, kv.second.toString()); }, "Set a number of options at once."); m.def("unsetOption", &xacc::unsetOption, ""); m.def("getOption", (const std::string (*)(const std::string &)) & xacc::getOption, "Get an XACC framework option."); m.def("hasAccelerator", &xacc::hasAccelerator, "Does XACC have the given Accelerator installed?"); m.def("hasCompiler", &xacc::hasCompiler, "Does XACC have the given Accelerator installed?"); m.def("optionExists", &xacc::optionExists, "Set an XACC framework option."); m.def("setIsPyApi", &xacc::setIsPyApi, "Indicate that this is using XACC via the Python API."); m.def("optimizeFunction", &xacc::optimizeFunction, "Run an optimizer on the given function."); m.def("analyzeBuffer", (void (*)(std::shared_ptr)) & xacc::analyzeBuffer, "Analyze the AcceleratorBuffer to produce problem-specific results."); m.def("getCache", &xacc::getCache, ""); m.def( "appendCache", (void (*)(const std::string, const std::string, InstructionParameter &,const std::string)) & xacc::appendCache, ""); m.def("Finalize", &xacc::Finalize, "Finalize the framework"); m.def( "loadBuffer", [](const std::string json) { std::istringstream s(json); auto buffer = std::make_shared(); buffer->load(s); return buffer; }, ""); m.def( "compileKernel", [](std::shared_ptr acc, const std::string &src, const std::string &compilerName = "") -> std::shared_ptr { if (!compilerName.empty()) xacc::setOption("compiler", compilerName); xacc::Program p(acc, src); p.build(); return p.getRuntimeKernels()[0].getIRFunction(); }, py::arg("acc"), py::arg("src"), py::arg("compilerName") = std::string(""), py::return_value_policy::move, ""); m.def( "functionToInstruction", [](std::shared_ptr f) { return std::dynamic_pointer_cast(f); }, py::return_value_policy::copy); py::module gatesub = m.def_submodule("gate", "Gate model quantum computing data structures."); gatesub.def( "create", [](const std::string &name, std::vector qbits, std::vector params = std::vector{}) -> std::shared_ptr { return xacc::getService("gate")->createInstruction( name, qbits, params); }, "Convenience function for creating a new GateInstruction.", py::arg("name"), py::arg("qbits"), py::arg("params") = std::vector{}); gatesub.def( "createFunction", [](const std::string &name, std::vector qbits, std::vector params = std::vector{}) -> std::shared_ptr { return xacc::getService("gate")->createFunction(name, qbits, params); }, "Convenience function for creating a new GateFunction.", py::arg("name"), py::arg("qbits"), py::arg("params") = std::vector{}); gatesub.def( "createIR", []() -> std::shared_ptr { return xacc::getService("gate")->createIR(); }, "Convenience function for creating a new GateIR."); gatesub.def( "getState", [](std::shared_ptr acc, std::shared_ptr f) { auto results = acc->getAcceleratorState(f); Eigen::VectorXcd ret = Eigen::Map(results.data(), results.size()); return ret; }, "Compute and return the state after execution of the given program on " "the given accelerator."); py::module aqcsub = m.def_submodule("dwave", "Gate model quantum computing data structures."); aqcsub.def( "create", [](const std::string &name, std::vector qbits, std::vector params = std::vector{}) -> std::shared_ptr { return xacc::getService("dwave")->createInstruction( name, qbits, params); }, "Convenience function for creating a new DWInstruction.", py::arg("name"), py::arg("qbits"), py::arg("params") = std::vector{}); aqcsub.def( "createFunction", [](const std::string &name, std::vector qbits, std::vector params = std::vector{}) -> std::shared_ptr { return xacc::getService("dwave")->createFunction( name, qbits, params); }, "Convenience function for creating a new DWFunction.", py::arg("name"), py::arg("qbits"), py::arg("params") = std::vector{}); aqcsub.def( "createIR", []() -> std::shared_ptr { return xacc::getService("dwave")->createIR(); }, "Convenience function for creating a new DWIR."); aqcsub.def( "embed", [](std::shared_ptr f, std::shared_ptr acc, const std::string algo) -> std::map> { auto a = xacc::getService(algo); auto hardwareconnections = acc->getAcceleratorConnectivity(); std::set nUniqueBits; for (auto& edge : hardwareconnections) { nUniqueBits.insert(edge.first); nUniqueBits.insert(edge.second); } int nBits = *std::max_element(nUniqueBits.begin(), nUniqueBits.end()) + 1; auto hardware = std::make_shared(nBits); for (auto& edge : hardwareconnections) { hardware->addEdge(edge.first, edge.second); } int maxBitIdx = 0; for (auto inst : f->getInstructions()) { if (inst->name() == "dw-qmi") { auto qbit1 = inst->bits()[0]; auto qbit2 = inst->bits()[1]; if (qbit1 > maxBitIdx) { maxBitIdx = qbit1; } if (qbit2 > maxBitIdx) { maxBitIdx = qbit2; } } } auto problemGraph = std::make_shared(maxBitIdx+1); for (auto inst : f->getInstructions()) { if (inst->name() == "dw-qmi") { auto qbit1 = inst->bits()[0]; auto qbit2 = inst->bits()[1]; if (qbit1 == qbit2) { problemGraph->setVertexProperties(qbit1, 1.0); } else { problemGraph->addEdge(qbit1, qbit2, 1.0); } } } return a->embed(problemGraph, hardware); }, ""); }