Commit 5e45c6fa authored by Mccaskey, Alex's avatar Mccaskey, Alex
Browse files

adding KernelBuilder.synthesize() that will take a unitary matrix or matrix...


adding KernelBuilder.synthesize() that will take a unitary matrix or matrix generator function and create an executable kernel

Signed-off-by: Mccaskey, Alex's avatarAlex McCaskey <mccaskeyaj@ornl.gov>
parent 57756388
Loading
Loading
Loading
Loading
Loading
+30 −12
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())
nq = 3
# We assume a default qreg...
builder = KernelBuilder()#kernel_args={'t':float}) 

builder.x(0)
builder.ry(1, ('t',0))
builder.cnot(1, 0) 

# builder.rx(1, ('x', 1))

#builder.measure_all()
ansatz = builder.create()

# q = qalloc(nq)
# ansatz(q, 2.2, [1.1,2.2])
# print(q.counts())
H = -2.1433 * X(0) * X(1) - 2.1433 * \
    Y(0) * Y(1) + .21829 * Z(0) - 6.125 * Z(1) + 5.907

n_params = 1
obj = createObjectiveFunction(ansatz, H, n_params)

# evaluate at a concrete set of params
vqe_energy = obj([.59])
print(vqe_energy) 

# Run full optimization
optimizer = createOptimizer('nlopt')
results = optimizer.optimize(obj)
print(results)
 No newline at end of file
+54 −0
Original line number Diff line number Diff line
from qcor import * 
import numpy as np 

set_qpu('qpp', {'shots':1024})

ccnot = np.eye(8)
ccnot[6,6] = 0.0
ccnot[7,7] = 0.0
ccnot[6,7] = 1.0
ccnot[7,6] = 1.0

# Synthesize the CCNOT kernel 
# using the KernelBuilder
builder = KernelBuilder()
[builder.x(i) for i in range(3)]
builder.synthesize(unitary=ccnot)
builder.measure(range(3))
ccnot_circuit = builder.create()

q = qalloc(3)
ccnot_circuit(q)
print(q.counts())

def generator(x : List[float]):
    # Must provide imports in the function!
    from scipy.sparse.linalg import expm
    from openfermion.ops import QubitOperator
    from openfermion.transforms import get_sparse_operator
    qop = QubitOperator('X0 Y1') - QubitOperator('Y0 X1')
    qubit_sparse = get_sparse_operator(qop)
    return expm(0.5j * x[0] * qubit_sparse).todense()

set_qpu('qpp')
b = KernelBuilder() # can pass this, kernel_args={'x':List[float]}), but will be inferred
b.x(0)
b.synthesize(unitary=generator, method='kak')
ansatz = b.create()

H = -2.1433 * X(0) * X(1) - 2.1433 * \
    Y(0) * Y(1) + .21829 * Z(0) - 6.125 * Z(1) + 5.907

# Create the VQE ObjectiveFunction, use a 
# central finite difference gradient strategy
vqe_obj = createObjectiveFunction(ansatz, H, 1, {'gradient-strategy':'central', 'step':1e-1})

# Create a gradient-based optimizer, l-bfgs
optimizer = createOptimizer('nlopt', {'initial-parameters':[.5], 'maxeval':10, 'algorithm':'l-bfgs'})

# Find the ground state via optimization
results = optimizer.optimize(vqe_obj)

# Print the results
print('Results:')
print(results)
 No newline at end of file
+64 −4
Original line number Diff line number Diff line
import sys, uuid, atexit
import sys, uuid, atexit, hashlib

if '@QCOR_APPEND_PLUGIN_PATH@':
    sys.argv += ['__internal__add__plugin__path', '@QCOR_APPEND_PLUGIN_PATH@']
@@ -624,6 +624,7 @@ class KernelBuilder(object):
        self.kernel_args = kwargs['kernel_args'] if 'kernel_args' in kwargs else {}
        # Returns list of tuples, (name, nRequiredBits, isParameterized)
        all_instructions = internal_get_all_instructions()
        all_instructions = [element for element in all_instructions if element[0] != 'Measure']
        self.qjit_str = ''
        self.qreg_name = 'q'
        self.TAB = '    '
@@ -642,8 +643,12 @@ class KernelBuilder(object):
        for arg in args:
            if isinstance(arg, str):
                params.append(str(arg))
                if str(arg) not in self.kernel_args:
                    self.kernel_args[str(arg)] = float
            elif isinstance(arg, tuple):
                params.append(arg[0]+'['+str(arg[1])+']')
                if arg[0] not in self.kernel_args:
                    self.kernel_args[arg[0]] = List[float]
            else:
                print('[KernelBuilder Error] Invalid parameter type.')
                exit(1)
@@ -657,7 +662,7 @@ class KernelBuilder(object):
        self.qjit_str += self.TAB+'{}({}, {{}})\\n'.format({}, params_str)
'''.format(name.lower(), qbits_str, isParameterized, name.lower(), name, qbits_indexed, qbits_str, name, qbits_indexed, qbits_str)
            # print(new_func_str)
            result = {}
            result = globals()
            exec (new_func_str.strip(), result)
            setattr(KernelBuilder, instruction[0].lower(), result[instruction[0].lower()])

@@ -665,7 +670,62 @@ class KernelBuilder(object):
        self.qjit_str += self.TAB + 'for i in range({}.size()):\n'.format(self.qreg_name)
        self.qjit_str += self.TAB+self.TAB+'Measure({}[i])\n'.format(self.qreg_name)

    # def measure(self, qbits):
    def measure(self, qbit): 
        if isinstance(qbit, range):
            qbit = list(qbit)
        if isinstance(qbit, list):
            for i in qbit:
                self.measure(i)
            # self.qjit_str += self.TAB + 'qbits = {}'.format(qbit)
            # self.qjit_str += self.TAB+'for i in range(qbits):\n'.format(qbit)
            # self.qjit_str += self.TAB+self.TAB+'Measure({}[i])\n'.format(self.qreg_name)
        elif isinstance(qbit, int):
            self.qjit_str += self.TAB+'Measure({}[{}])\n'.format(self.qreg_name, qbit)
        else:
            print('[KernelBuilder] invalid input to measure {}'.format(qbit))
            exit(1)

    # Synthesis from matrix, or from matrix generator
    # can provide method = [qsearch,qfast,kak, etc.]
    def synthesize(self, **kwargs):
        method = ''
        if 'method' in kwargs:
            method = kwargs['method']

        if 'unitary' not in kwargs:
            print('[KernelBuilder Error] Please pass unitary=matrix_var kwarg.')
        
        unitary = kwargs['unitary']

        if hasattr(unitary, '__call__'):
            fbody_src = '\n'.join(inspect.getsource(unitary).split('\n')[1:])
            hash_object = hashlib.md5(fbody_src.encode('utf-8'))
            arg_names, _, _, _, _, _, type_annotations = inspect.getfullargspec(unitary)

            # add arguments if not in self.kernel_args
            for arg, t in type_annotations.items():
                if arg not in self.kernel_args:
                    # print('[KernelBuilder] Found argument in unitary generator that is unknown ({}). Adding it to the kernel args.'.format(arg))
                    self.kernel_args[arg] = t


            mat_var_name = '__internal_matrix_data_'+str(hash_object.hexdigest())
            self.qjit_str += self.TAB + 'with decompose(q{}) as {}:\n'.format(','+method if method!='' else method, mat_var_name)
            for line in fbody_src.split('\n'):
                line = ' '.join(line.split())
                if 'return' in line:
                    line = line.replace('return', mat_var_name+' =')
                self.qjit_str += self.TAB + self.TAB + line + '\n'

        elif hasattr(unitary, '__iter__'):
            unitary_str = ' ; '.join([str(row).replace('[','').replace(']','').replace(' ', ', ') for row in unitary])
            hash_object = hashlib.md5(unitary_str.encode('utf-8'))
            mat_var_name = '__internal_matrix_data_'+str(hash_object.hexdigest())
            self.qjit_str += self.TAB + 'with decompose(q) as {}:\n'.format(mat_var_name)
            self.qjit_str += self.TAB+self.TAB+'{} = np.matrix("{}")\n'.format(mat_var_name, unitary_str)
        else:
            print('[KernelBuilder Error] Cannot parse this type of unitary matrix')
            exit(1)

    def create(self):
        # print(self.qjit_str)
+18 −0
Original line number Diff line number Diff line
@@ -347,6 +347,24 @@ QJIT::QJIT() {
}
void QJIT::write_cache() {
  std::string cache_file_loc = qjit_cache_path + "/qjit_cache.json";

  // MAKE SURE WE DONT OVERWRITE
  // read in any existing data, and merge
  std::ifstream cache_file(cache_file_loc);
  std::string cache_file_contents((std::istreambuf_iterator<char>(cache_file)),
                                  std::istreambuf_iterator<char>());
  if (!cache_file_contents.empty()) {
    auto cache_json = nlohmann::json::parse(cache_file_contents);
    auto jit_cache = cache_json["jit_cache"];
    std::map<std::size_t, std::string> tmp;
    for (auto &element : jit_cache) {
      auto key_val = element.get<std::pair<std::size_t, std::string>>();
      tmp.insert(key_val);
    }
    cached_kernel_codes.merge(tmp);
  }
  /////

  nlohmann::json j;
  j["jit_cache"] = cached_kernel_codes;
  auto str = j.dump();