Source code for qubiter.adv_applications.MeanHamil_rigetti

import copy as cp

from qubiter.adv_applications.MeanHamil import *
from qubiter.device_specific.Qubiter_to_RigettiPyQuil import *
from qubiter.device_specific.RigettiTools import *
import qubiter.utilities_gen as utg

from openfermion.ops import QubitOperator

from pyquil.quil import Program, Pragma
from pyquil.gates import *
from pyquil.api import WavefunctionSimulator

[docs]class MeanHamil_rigetti(MeanHamil): """ This class is a child of MeanHamil. This class uses either Rigetti's real hardware or virtual simulator to calculate mean values. `qc` returned by Rigetti's get_qc() method is passed in as an input to the constructor of this class. If num_samples !=0, the class uses to calculate mean values. If num_samples=0, the class ignores the `qc` input and uses PyQuil's WavefunctionSimulator to calculate mean values exactly. Attributes ---------- do_resets : bool pg : Program object of PyQuil class `Program` qc : QuantumComputer returned by PyQuil method get_qc() term_to_exec : dict[] maps a term to an executable. QubitOperator from OpenFermion has attribute `terms` which is a dict from a term to a coefficient. An executable is the output of PyQuil's compile() method. translation_line_list : list[str] a list of lines of PyQuil code generated by the translator. The lines all start with "pg +=" translator : Qubiter_to_RigettiPyQuil """
[docs] def __init__(self, qc, file_prefix, num_qbits, hamil, all_var_nums, fun_name_to_fun, do_resets=True, **kwargs): """ Constructor Do in constructor as much hamil indep stuff as possible so don't have to redo it with every call to cost fun. Also, when self.num_samples !=0, we store a dict called term_to_exec mapping an executable (output of Rigetti compile() function) to a term, for each term in the hamiltonian hamil. When num_samples=0, term_to_exec={} Parameters ---------- qc : QuantumComputer file_prefix : str num_qbits : int hamil : QubitOperator all_var_nums : list[int] fun_name_to_fun : dict[str, function] do_resets : bool kwargs : dict key-words args of MeanHamilMinimizer constructor Returns ------- """ MeanHamil.__init__(self, file_prefix, num_qbits, hamil, all_var_nums, fun_name_to_fun, **kwargs) self.qc = qc self.do_resets = do_resets # this creates a file with all PyQuil gates that # are independent of hamil. Gates may contain free parameters self.translator = Qubiter_to_RigettiPyQuil( self.file_prefix, self.num_qbits, aqasm_name='RigPyQuil', prelude_str='', ending_str='') with open(utg.preface(self.translator.aqasm_path), 'r') as fi: self.translation_line_list = fi.readlines() pg = Program() = pg if self.num_samples: # pg prelude pg += Pragma('INITIAL_REWIRING', ['"PARTIAL"']) if self.do_resets: pg += RESET() ro = pg.declare('ro', 'BIT', self.num_qbits) s = '' for var_num in self.all_var_nums: vname = self.translator.vprefix + str(var_num) s += vname s += ' = pg.declare("' s += vname s += '", memory_type="REAL")\n' exec(s) # add to pg the operations that are independent of hamil for line in self.translation_line_list: line = line.strip('\n') if line: exec(line) len_pg_in = len(pg) # hamil loop to store executables for each term in hamil self.term_to_exec = {} for term, coef in self.hamil.terms.items(): # reset pg to initial length. # Temporary work-around to bug # in PyQuil ver 2.5.0. # Slicing was changing # pg from type Program to type list pg = Program(pg[:len_pg_in]) = pg # add xy measurements coda to pg bit_pos_to_xy_str =\ {bit: action for bit, action in term if action != 'Z'} RigettiTools.add_xy_meas_coda_to_program( pg, bit_pos_to_xy_str) # request measurements for i in range(self.num_qbits): pg += MEASURE(i, ro[i]) pg.wrap_in_numshots_loop(shots=self.num_samples) executable = self.qc.compile(pg) # print(",,,...", executable) self.term_to_exec[term] = executable
[docs] def get_mean_val(self, var_num_to_rads): """ This method returns the empirically determined Hamiltonian mean value. It takes as input the values of placeholder variables. It passes those values into the Rigetti method run() when num_samples !=0. When num_samples=0, WavefunctionSimulator is used to calculate the output mean value exactly. Parameters ---------- var_num_to_rads : dict[int, float] Returns ------- float """ # hamil loop mean_val = 0 for term, coef in self.hamil.terms.items(): # we have checked before that coef is real coef = complex(coef).real vprefix = self.translator.vprefix var_name_to_rads = {vprefix + str(vnum): [rads] for vnum, rads in var_num_to_rads.items()} if self.num_samples: # send and receive from cloud, get obs_vec bitstrings =[term], memory_map=var_name_to_rads) obs_vec = RigettiTools.obs_vec_from_bitstrings( bitstrings, self.num_qbits, bs_is_array=True) # go from obs_vec to effective state vec counts_dict = StateVec.get_counts_from_obs_vec(self.num_qbits, obs_vec) emp_pd = StateVec.get_empirical_pd_from_counts(self.num_qbits, counts_dict) effective_st_vec = StateVec.get_emp_state_vec_from_emp_pd( self.num_qbits, emp_pd) else: # num_samples = 0 sim = WavefunctionSimulator() pg = Program() # don't know how to declare number of qubits # so do this for k in range(self.num_qbits): pg += I(k) for key, val in var_name_to_rads.items(): exec(key + '=' + str(val[0])) for line in self.translation_line_list: line = line.strip('\n') if line: exec(line) bit_pos_to_xy_str =\ {bit: action for bit, action in term if action != 'Z'} RigettiTools.add_xy_meas_coda_to_program( pg, bit_pos_to_xy_str) st_vec_arr = sim.wavefunction(pg).amplitudes st_vec_arr = st_vec_arr.reshape([2]*self.num_qbits) perm = list(reversed(range(self.num_qbits))) st_vec_arr = np.transpose(st_vec_arr, perm) effective_st_vec = StateVec(self.num_qbits, st_vec_arr) # add contribution to mean real_arr = self.get_real_vec(term) mean_val += coef*effective_st_vec.\ get_mean_value_of_real_diag_mat(real_arr) return mean_val
if __name__ == "__main__": def main(): pass