Commit b05e7ca7 authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

first pass at prototype python KernelBuilder API

parent 99e88ab3
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
from qcor import * 

nq = 10
builder = KernelBuilder() 

builder.h(0)
for i in range(nq-1):
    builder.cnot(i, i+1)
builder.measure_all()
ghz = builder.create()

q = qalloc(nq)
ghz(q)
print(q.counts())
+11 −0
Original line number Diff line number Diff line
@@ -694,6 +694,17 @@ PYBIND11_MODULE(_pyqcor, m) {
      },
      "");

  m.def("internal_get_all_instructions", []() -> std::vector<py::tuple> {
    auto insts =  xacc::getServices<xacc::Instruction>();
    std::vector<py::tuple> ret;
    for (auto inst : insts) {
      if (!inst->isComposite()) {
        ret.push_back(py::make_tuple(inst->name(), inst->nRequiredBits(), inst->isParameterized()));
      }
    }
    return ret;
  });

  // QuaSiMo sub-module bindings:
  {
    py::module qsim = m.def_submodule("QuaSiMo", "QCOR's python QuaSiMo submodule");
+63 −4
Original line number Diff line number Diff line
import sys,  atexit
import xacc, sys,  atexit

if '@QCOR_APPEND_PLUGIN_PATH@':
    sys.argv += ['__internal__add__plugin__path', '@QCOR_APPEND_PLUGIN_PATH@']

import xacc

from _pyqcor import *
import inspect, ast
from typing import List
@@ -197,6 +195,9 @@ class qjit(object):
        self.extra_cpp_code = ''

        # Get the kernel function body as a string
        if '__internal_fbody_src_provided__' in kwargs:
            fbody_src = kwargs['__internal_fbody_src_provided__']
        else:
            fbody_src = '\n'.join(inspect.getsource(self.function).split('\n')[2:])

        # Get the arg variable names and their types
@@ -594,6 +595,64 @@ class qjit(object):

        return

class KernelBuilder(object):
    """
    The QCOR KernelBuilder is a high-level data structure that enables the 
    development of qcor quantum kernels programmatically in Python. Example usage:

    from qcor import * 

    nq = 10
    builder = KernelBuilder() 

    builder.h(0)
    for i in range(nq-1):
        builder.cnot(i, i+1)
    builder.measure_all()
    ghz = builder.create()

    q = qalloc(nq)
    ghz(q)
    print(q.counts())

    """
    def __init__(self, py_args_dict : dict = {}):
        self.kernel_args = py_args_dict 
        all_instructions = internal_get_all_instructions()
        self.qjit_str = ''
        self.qreg_name = 'q'
        TAB = '    '

        for instruction in all_instructions:
            isParameterized = instruction[2]
            n_bits = instruction[1]
            name = instruction[0]
            if not instruction[2]:
                # No parameters...
                # set it as a method on this class
                qbits_str = ','.join(['q{}'.format(i) for i in range(n_bits)])
                new_func_str = 'def {}(self, {}):\n'.format(instruction[0].lower(),qbits_str)
                qbit_str = ','.join([])
                new_func_str += TAB +"self.qjit_str += '    {}(".format(name)+','.join(
                    ["{}[{{}}]".format(self.qreg_name) for i in range(n_bits)])+")\\n'.format({})".format(qbits_str)
                # print(new_func_str)
                result = {}
                exec (new_func_str.strip(), result)
                setattr(KernelBuilder, instruction[0].lower(), result[instruction[0].lower()])

    def measure_all(self):
        self.qjit_str += '    for i in range({}.size()):\n'.format(self.qreg_name)
        self.qjit_str += '        Measure({}[i])\n'.format(self.qreg_name)

    def create(self):
        # print(self.qjit_str)
        result = globals()
        exec('def test(q : qreg):\n'+self.qjit_str, result)
        # print(result)
        function = result['test']
        _qjit = qjit(function, __internal_fbody_src_provided__ = self.qjit_str)
        return _qjit
        

# Must have qpu in the init kwargs
# defaults to qpp, but will search for -qpu flag
+21 −0
Original line number Diff line number Diff line
@@ -79,6 +79,14 @@ class QuantumKernel {
    os << derived.parent_kernel->toString() << "\n";
  }

  static void print_kernel(Args... args) {
    Derived derived(args...);
    derived.disable_destructor = true;
    derived(args...);
    xacc::internal_compiler::execute_pass_manager();
    std::cout << derived.parent_kernel->toString() << "\n";
  }

  // Static method to query how many instructions are in this kernel
  static std::size_t n_instructions(Args... args) {
    Derived derived(args...);
@@ -232,6 +240,8 @@ class QuantumKernel {
          "Unable to observe kernels that already have Measure operations.");
    }

    xacc::internal_compiler::execute_pass_manager();

    // 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);
@@ -256,11 +266,22 @@ class QuantumKernel {
          "Unable to observe kernels that already have Measure operations.");
    }

    xacc::internal_compiler::execute_pass_manager();

    // 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(derived.parent_kernel, obs, q);
  }

  static std::string openqasm(Args... args) {
    Derived derived(args...);
    derived.disable_destructor = true;
    derived(args...);
    xacc::internal_compiler::execute_pass_manager();
    return __internal__::translate("staq", derived.parent_kernel);
  }

  virtual ~QuantumKernel() {}
};

+1 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ void PassManager::applyPlacement(
      return "default-placement";
    }
    // Use the specified placement if any.
    // Fall back on QPU specified placement if no placement specified by user
    // Note: placement will only be activated if the accelerator
    // has a connectivity graph.
    return m_placement.empty()
Loading