xacc-py.cpp 17.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
/*******************************************************************************
 * 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
 *******************************************************************************/
13
#include "XACC.hpp"
14
15
#include "IRGenerator.hpp"

16
#include <pybind11/complex.h>
17
#include <pybind11/numpy.h>
18
19
20
21
22
23
24
25
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/eigen.h>
#include <pybind11/iostream.h>
#include <pybind11/operators.h>

#include "GateInstruction.hpp"
#include "GateFunction.hpp"
26
#include "GateIR.hpp"
27
28
29
30
31

namespace py = pybind11;
using namespace xacc;
using namespace xacc::quantum;

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// `boost::variant` as an example -- can be any `std::variant`-like container
namespace pybind11 { namespace detail {
    template <typename... Ts>
    struct type_caster<boost::variant<Ts...>> : variant_caster<boost::variant<Ts...>> {};

    // Specifies the function used to visit the variant -- `apply_visitor` instead of `visit`
    template <>
    struct visit_helper<boost::variant> {
        template <typename... Args>
        static auto call(Args &&...args) -> decltype(boost::apply_visitor(args...)) {
            return boost::apply_visitor(args...);
        }
    };
}} // namespace pybind11::detail

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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 "";//PYBIND11_OVERLOAD_PURE(const std::string, xacc::Accelerator, description);
    }

    void initialize() override {
        return; //PYBIND11_OVERLOAD_PURE(void, xacc::Accelerator, initialize);
    }
    
    AcceleratorType getType() override {
        return Accelerator::AcceleratorType::qpu_gate;//PYBIND11_OVERLOAD_PURE(AcceleratorType, xacc::Accelerator, getType);
    }
    
    std::vector<std::shared_ptr<IRTransformation>> getIRTransformations() override {
        return {};
    }

    bool isValidBufferSize(const int n) override {return true;}
    
    /* Trampoline (need one for each virtual function) */
    void execute(std::shared_ptr<xacc::AcceleratorBuffer> buf, std::shared_ptr<xacc::Function> f) override {
        PYBIND11_OVERLOAD_PURE(
            void, /* Return type */
            xacc::Accelerator,      /* Parent class */
            execute,          /* Name of function in C++ (must match Python name) */
            buf, 
            f
        );
    }

    std::vector<std::shared_ptr<AcceleratorBuffer>> execute(
			std::shared_ptr<AcceleratorBuffer> buffer,
			const std::vector<std::shared_ptr<Function>> functions) override {
        return {};
    }
    	
    std::shared_ptr<AcceleratorBuffer> createBuffer(
				const std::string& varId) override {
        return std::make_shared<AcceleratorBuffer>(varId, 100);
    }

    std::shared_ptr<AcceleratorBuffer> createBuffer(
				const std::string& varId, const int size) override {
        return std::make_shared<AcceleratorBuffer>(varId, size);
    }
};

102
PYBIND11_MODULE(_pyxacc, m) {
103
104
105
106
    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.";

107
    py::class_<xacc::InstructionParameter> (m, "InstructionParameter",
108
109
			"The InstructionParameter provides a variant structure "
					"to provide parameters to XACC Instructions at runtime. "
110
111
112
113
114
115
116
117
					"This type can be an int, double, float, string, or complex value.")
		.def(py::init<int>(), "Construct as an int.")
		.def(py::init<double>(), "Construct as a double.")
		.def(py::init<std::string>(), "Construct as a string.")
		.def(py::init<std::complex<double>>(), "Construct as a complex double.")
		.def(py::init<float>(), "Construct as a float.");

    py::class_<xacc::Instruction, std::shared_ptr<xacc::Instruction>>(m, "Instruction", "")
118
119
120
121
122
123
124
125
126
127
128
    	.def("nParameters", &xacc::Instruction::nParameters, "")
    	.def("toString", &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("getTag", &xacc::Instruction::getTag, "")
    	.def("name", &xacc::Instruction::name, "")
129
130
131
		.def("description", &xacc::Instruction::description, "");

    py::class_<xacc::Function, std::shared_ptr<xacc::Function>>(m, "Function", "")
132
    	.def("nInstructions", &xacc::Function::nInstructions, "")
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
		.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("eval", &xacc::Function::operator(), "")
		.def("name", &xacc::Function::name, "")
		.def("description", &xacc::Function::description, "")
		.def("nParameters", &xacc::Function::nParameters, "")
		.def("toString", &xacc::Function::toString, "")
		.def("getParameter", &xacc::Function::getParameter, "")
		.def("getParameters", &xacc::Function::getParameters, "")
		.def("setParameter", &xacc::Function::setParameter, "")
Mccaskey, Alex's avatar
Mccaskey, Alex committed
148
149
		.def("getTag", &xacc::Function::getTag, "")
		.def("mapBits", &xacc::Function::mapBits, "");
150
151

    // Expose the IR interface
152
153
    py::class_<xacc::IR, std::shared_ptr<xacc::IR>> (m, "IR", "The XACC Intermediate Representation, "
    		"serves as a container for XACC Functions.")
154
155
    	.def("getKernels", &xacc::IR::getKernels, "Return the kernels in this IR")
		.def("addKernel", &xacc::IR::addKernel, "");
Mccaskey, Alex's avatar
Mccaskey, Alex committed
156

157
158
159
160
161
162
163
164
165
    py::class_<xacc::InstructionIterator>(m, "InstructionIterator", "")
        .def(py::init<std::shared_ptr<xacc::Function>>())
        .def("hasNext", &xacc::InstructionIterator::hasNext, "")
        .def("next", &xacc::InstructionIterator::next, "");
        
    py::class_<xacc::IRPreprocessor, std::shared_ptr<xacc::IRPreprocessor>> (m, "IRPreprocesor", "")
        .def("process", &xacc::IRPreprocessor::process, "");
    py::class_<xacc::AcceleratorBufferPostprocessor, std::shared_ptr<xacc::AcceleratorBufferPostprocessor>> (m, "AcceleratorBufferPostprocessor", "")
        .def("process", &xacc::AcceleratorBufferPostprocessor::process, "");
166

167
    py::class_<xacc::IRGenerator, std::shared_ptr<xacc::IRGenerator>>(m, "IRGenerator", "")
168
169
170
171
172
173
        .def("generate", (std::shared_ptr<xacc::Function> (xacc::IRGenerator::*)(
				std::vector<xacc::InstructionParameter>)) &xacc::IRGenerator::generate, 
                py::return_value_policy::reference, "")
        .def("generate", (std::shared_ptr<xacc::Function> (xacc::IRGenerator::*)(
				std::map<std::string, xacc::InstructionParameter>)) &xacc::IRGenerator::generate, 
                py::return_value_policy::reference, "");
174
                    
175
    // Expose the Kernel
176
177
178
179
180
181
182
    py::class_<xacc::Kernel<>, std::shared_ptr<xacc::Kernel<>>>(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<xacc::AcceleratorBuffer>,
				std::vector<xacc::InstructionParameter>)) &xacc::Kernel<>::operator()
183
					, "Execute this Kernel with the given set of "
184
185
							"InstructionParamters. This set can be empty "
							"if there are no parameters.");
186

187
188
189
	py::class_<xacc::KernelList<>>(m, "KernelList", "The XACC KernelList is a vector of "
			"Kernels that provides a operator() implementation to execute multiple kernels at once.")
		.def("execute",
190
191
			(std::vector<std::shared_ptr<xacc::AcceleratorBuffer>> (xacc::KernelList<>::*)(
					std::shared_ptr<xacc::AcceleratorBuffer>,
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
					std::vector<xacc::InstructionParameter>)) &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__",
207
208
209
210
211
212
213
214
215
216
217
			[]( 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;
			});
218

219
	// Expose the Accelerator
220
	py::class_<xacc::Accelerator, std::shared_ptr<xacc::Accelerator>, PyAccelerator> acc(m,
221
222
			"Accelerator", "Accelerator wraps the XACC C++ Accelerator class "
					"and provides a mechanism for creating buffers of qubits. Execution "
223
224
					"is handled by the XACC Kernels.");
    acc.def(py::init<>()).def("name", &xacc::Accelerator::name, "Return the name of this Accelerator.")
225
226
		.def("createBuffer", (std::shared_ptr<xacc::AcceleratorBuffer> (xacc::Accelerator::*)(const std::string&, const int))
					&xacc::Accelerator::createBuffer, py::return_value_policy::reference,
227
228
229
230
			"Return a newly created register of qubits of the given size.")
		.def("createBuffer", (std::shared_ptr<xacc::AcceleratorBuffer> (xacc::Accelerator::*)(const std::string&))
					&xacc::Accelerator::createBuffer, py::return_value_policy::reference,
			"Return a newly allocated register of all qubits on the Accelerator.")
231
		.def("execute", (void (xacc::Accelerator::*)(std::shared_ptr<AcceleratorBuffer>, std::shared_ptr<Function>)) &xacc::Accelerator::execute, "Execute the Function with the given AcceleratorBuffer.");
232

233
234
235
236
237
    py::enum_<Accelerator::AcceleratorType>(acc, "AcceleratorType")
        .value("qpu_aqc", Accelerator::AcceleratorType::qpu_aqc)
        .value("qpu_gate", Accelerator::AcceleratorType::qpu_gate)
        .export_values();
        
238
	// Expose the AcceleratorBuffer
239
240
	py::class_<xacc::AcceleratorBuffer, std::shared_ptr<xacc::AcceleratorBuffer>>(m,
			"AcceleratorBuffer", "The AcceleratorBuffer models a register of qubits.")
241
		.def(py::init<const std::string&,const int>())
242
243
		.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.")
244
		.def("appendMeasurement", (void (xacc::AcceleratorBuffer::*)(const std::string&)) &xacc::AcceleratorBuffer::appendMeasurement, "Append the measurement string")
245
246
247
		.def("getMeasurementStrings", &xacc::AcceleratorBuffer::getMeasurementStrings, "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.");
248
249

	// Expose the Compiler
250
	py::class_<xacc::Compiler, std::shared_ptr<xacc::Compiler>>(m,
251
			"Compiler", "The XACC Compiler takes as input quantum kernel source code, "
252
253
254
255
256
					"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::IR> (xacc::Compiler::*)(const std::string&, std::shared_ptr<xacc::Accelerator>)) &xacc::Compiler::compile, "Compile the "
			"given source code against the given Accelerator.")
		.def("translate", &xacc::Compiler::translate, "Translate the given IR Function instance to source code in this Compiler's language.");
257
258

	// Expose the Program object
259
	py::class_<xacc::Program>(m,
260
			"Program", "The Program is the primary entrypoint for compilation and execution in XACC. Clients provide quantum kernel source "
261
262
263
					"code and the Accelerator instance, and the Program handles compiling the code and provides Kernel instances to execute.")
		.def(py::init<std::shared_ptr<xacc::Accelerator>, const std::string &>(), "The constructor")
		.def(py::init<std::shared_ptr<xacc::Accelerator>, std::shared_ptr<xacc::IR>>(), "The constructor")
Mccaskey, Alex's avatar
Mccaskey, Alex committed
264
265
		.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.")
266
267
268
269
		.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");
270
271
272
273
274
275
276
277
278

    // Expose XACC API functions
	m.def("Initialize", (void (*)(std::vector<std::string>))
		&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("getAccelerator", (std::shared_ptr<xacc::Accelerator> (*)(const std::string&))
279
			&xacc::getAccelerator, py::return_value_policy::reference,
280
281
			"Return the accelerator with given name.");
	m.def("getCompiler", (std::shared_ptr<xacc::Compiler> (*)(const std::string&))
282
			&xacc::getCompiler, py::return_value_policy::reference,
283
			"Return the Compiler of given name.");
Mccaskey, Alex's avatar
Mccaskey, Alex committed
284
285
	m.def("getIRPreprocessor", (std::shared_ptr<xacc::IRPreprocessor> (*)(const std::string&))
			&xacc::getService<IRPreprocessor>, py::return_value_policy::reference,
286
287
288
289
			"Return the IRPreprocessor of given name.");
    m.def("getIRGenerator", (std::shared_ptr<xacc::IRGenerator> (*)(const std::string&))
			&xacc::getService<IRGenerator>, py::return_value_policy::reference,
			"Return the IRGenerator of given name.");
290
291
	m.def("setOption", &xacc::setOption, "Set an XACC framework option.");
	m.def("getOption", &xacc::getOption, "Get an XACC framework option.");
Mccaskey, Alex's avatar
Mccaskey, Alex committed
292
293
    m.def("hasAccelerator", &xacc::hasAccelerator, "Does XACC have the given Accelerator installed?");
    m.def("hasCompiler", &xacc::hasCompiler, "Does XACC have the given Accelerator installed?");
294
295
296
	m.def("optionExists", &xacc::optionExists, "Set an XACC framework option.");
	m.def("Finalize", &xacc::Finalize, "Finalize the framework");

297
298
299
300
301
302
303
	m.def("compileKernel", [](std::shared_ptr<Accelerator> acc, const std::string& src, const std::string& compilerName = "") -> std::shared_ptr<Function>{
		    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, "");
	
Mccaskey, Alex's avatar
Mccaskey, Alex committed
304
305
	m.def("functionToInstruction", [](std::shared_ptr<Function> f) {return std::dynamic_pointer_cast<Instruction>(f);}, py::return_value_policy::copy);

306
	py::module gatesub = m.def_submodule("gate", "Gate model quantum computing data structures.");
307
308
309
310
311
	gatesub.def("create",
			[](const std::string& name, std::vector<int> qbits, std::vector<InstructionParameter> params = std::vector<InstructionParameter> {}) -> std::shared_ptr<Instruction> {
				return xacc::getService<IRProvider>("gate")->createInstruction(name, qbits, params);
			}, "Convenience function for creating a new GateInstruction.",
			py::arg("name"), py::arg("qbits"),
312
			py::arg("params") = std::vector<InstructionParameter> { });
313
314
315
316
317
318
319
320
321
322
	gatesub.def("createFunction",
			[](const std::string& name, std::vector<int> qbits, std::vector<InstructionParameter> params = std::vector<InstructionParameter> {}) -> std::shared_ptr<Function> {
				return xacc::getService<IRProvider>("gate")->createFunction(name, qbits, params);
			}, "Convenience function for creating a new GateFunction.",
			py::arg("name"), py::arg("qbits"),
			py::arg("params") = std::vector<InstructionParameter> { });
	gatesub.def("createIR",
			[]() -> std::shared_ptr<IR> {
				return xacc::getService<IRProvider>("gate")->createIR();
			}, "Convenience function for creating a new GateIR.");
323

324
    gatesub.def("getState", 
325
            [](std::shared_ptr<Accelerator> acc, std::shared_ptr<Function> f) {
326
                auto results = acc->getAcceleratorState(f);
327
328
                Eigen::VectorXcd ret = Eigen::Map<Eigen::VectorXcd>(results.data(), results.size());
                return ret;
329
330
            }, "Compute and return the state after execution of the given program on the given accelerator.");

331
332
333
}