def test_vqe_run(): """VQE initialized and then minimizer is called to return result. Checks correct sequence of execution""" def param_prog(alpha): return Program([H(0), RZ(alpha)(0)]) hamiltonian = np.array([[1, 0], [0, -1]]) initial_param = 0.0 minimizer = Mock(spec=minimize, func_code=minimize.func_code) fake_result = Mock() fake_result.fun = 1.0 fake_result.x = [0.0] fake_result.status = 0 # adding so we avoid writing to logger minimizer.return_value = fake_result # not actually called in VQE run since we are overriding minmizer to just # return a value. Still need this so I don't try to call the QVM server. fake_qvm = Mock(spec=['wavefunction']) inst = VQE(minimizer) t_result = inst.vqe_run(param_prog, hamiltonian, initial_param, qvm=fake_qvm) assert np.isclose(t_result.fun, 1.0)
def get_angles(self): """ Finds optimal angles with the quantum variational eigensolver method. Stored VQE result :returns: ([list], [list]) A tuple of the beta angles and the gamma angles for the optimal solution. """ stacked_params = np.hstack((self.betas, self.gammas)) vqe = VQE(self.minimizer, minimizer_args=self.minimizer_args, minimizer_kwargs=self.minimizer_kwargs) cost_ham = reduce(lambda x, y: x + y, self.cost_ham) # maximizing the cost function! param_prog = self.get_parameterized_program() result = vqe.vqe_run(param_prog, cost_ham, stacked_params, qvm=self.qvm, **self.vqe_options) self.result = result betas = result.x[:self.steps] gammas = result.x[self.steps:] return betas, gammas
def __init__(self, QM, num_visible, num_hidden, n_quantum_measurements=None, verbose=False): """ create an RBM with the specified number of visible and hidden units Params ------------------------------------------------------------- QM: (rigetti QVM connection) QVM connection for which to use for quantum circuit simulation num_visible: (int) Number of visible units in RBM num_hidden (int) Number of hidden units in RBM n_quantum_measurements: (int) Number of measuremants to use for Quantum expectation estimation (default to None which does analytical) verbose: (bool) Verbosity of qRBM -------------------------------------------------------------- """ self.n_visible = num_visible self.n_hidden = num_hidden self.qvm = QM self.verbose = verbose self.n_quantum_measurements = n_quantum_measurements #tweak at your leisure. self.n_qaoa_steps = 1 self.beta_temp = 2.0 #don't tweak below here unless you know what you're doing. # only want this for built in expectation calculations... self.vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) self.state_prep_angle = np.arctan(np.e**(-self.beta_temp / 2.0)) * 2.0 self.WEIGHTS = np.asarray( np.random.uniform( low=-0.1 * np.sqrt(6. / (num_hidden + num_visible)), high=0.1 * np.sqrt(6. / (num_hidden + num_visible)), size=(num_visible, num_hidden))) # IN THIS VERSION BIAS IS UNUSED! self.BIAS = np.asarray( np.random.uniform( low=-0.1 * np.sqrt(6. / (num_hidden + num_visible)), high=0.1 * np.sqrt(6. / (num_hidden + num_visible)), size=(num_hidden))) #BIASES ARE ON HIDDENS. # W[i][j] = ith visible to jth hidden # Bias[j] = bias on jth hidden. self.pred_list = [] self.energy_list = []
def __init__(self, qvm=None, num_visible=2, num_hidden=1, steps=3, temp=1.0, quant_meas_num=None, bias=False, reduced=False): """ create an RBM with the specified number of visible and hidden units Params ------------------------------------------------------------- qvm: (Rigetti QVM connection) Simulator, num_visible: (int) Number of visible units, num_hidden: (int) Number of hidden units, steps: (int) Number of steps for QAOA, temp: (float) Temperature of the system, quant_meas_num: (int) Number of measuremants to use for Quantum expectation estimation. -------------------------------------------------------------- """ # Initializing the Params self.visible_units = num_visible self.hidden_units = num_hidden self.total_units = self.visible_units + self.hidden_units self.qvm = make_qvm(qvm) self.quant_meas_num = quant_meas_num self.qaoa_steps = steps self.beta_temp = temp self.state_prep_angle = np.arctan(np.exp(-1 / self.beta_temp)) * 2.0 self.vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) self.param_wb = 0.1 * np.sqrt(6. / self.total_units) self.WEIGHTS = np.asarray( np.random.uniform(low=-self.param_wb, high=self.param_wb, size=(num_visible, num_hidden))) # Using Reduced or Full Botlzman machines. if reduced: self.reduced = True else: self.reduced = False # Using Bias or not. if bias: self.BIAS = np.asarray( np.random.uniform(low=-self.param_wb, high=self.param_wb, size=(self.hidden_units))) else: self.BIAS = None
def __init__(self, q, rx, ry, rz): """ Constructor function for 1qubit MoMGrad. Build a random 1qubt rotation and then use the train function to learn it. params: ------- q: SyncConnection -> qvm connection to use for everything rx: float -> x pauli param (scaled up by a factor of pi) ry: float -> y pauli param (scaled up by a factor of pi) rz: float -> z pauli param (scaled up by a factor of pi) """ self.qvm = q # LAZY, we only use this for expectation calculations and not actual # VQE stuff. from scipy.optimize import minimize self.vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) # DON"T CHANGE BELOW UNLESS YOU KNOW WHAT YOU"RE DOING! # Initialize the indices and statistics for our Oscillators # Indices that our oscialltor will be simulated on self.oscialltor0 = [0, 1, 2] # Initial mu and sigma values for our oscillator self.oscialltor0_stats = [0.0, 0.95] self.oscialltor1 = [3, 4, 5] self.oscialltor1_stats = [0.0, 0.95] self.oscialltor2 = [6, 7, 8] self.oscialltor2_stats = [0.0, 0.95] # Our data qubits for the process we want to learn. self.datum_qubits = [9] # Our chosen learning rate. self.learning_rate = 0.3 # The X,Y,Z params we want to leanr for our abitrary unitary. # Note if learning doesn't go so well there may have been an # internal phase overflow, so be careful.... self.THETA1 = float(rx) # 0.785 self.THETA2 = float(ry) self.THETA3 = float(rz) # 0.906 # Lists to track our learning progress self.param_values = [] self.fid_values = [] # Number of samples to draw for each iteration of learning. self.n_samples = 400
def test_vqe_run(): """ VQE initialized and then minimizer is called to return result. Checks correct sequence of execution. """ def param_prog(alpha): return Program([H(0), RZ(alpha)(0)]) hamiltonian = np.array([[1, 0], [0, -1]]) initial_param = 0.0 minimizer = MagicMock(spec=minimize, func_code=minimize.__code__) fake_result = Mock() fake_result.fun = 1.0 fake_result.x = [0.0] fake_result.status = 0 # adding so we avoid writing to logger minimizer.return_value = fake_result # not actually called in VQE run since we are overriding minmizer to just # return a value. Still need this so I don't try to call the QVM server. fake_qvm = Mock(spec=['wavefunction']) with patch("funcsigs.signature") as patch_signature: func_sigs_fake = MagicMock(spec=funcsigs.Signature) func_sigs_fake.parameters.return_value = \ OrderedDict({ 'fun': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'x0': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'args': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'method': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'jac': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'hess': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'hessp': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'bounds': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'constraints': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'tol': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'callback': funcsigs.Parameter.POSITIONAL_OR_KEYWORD, 'options': funcsigs.Parameter.POSITIONAL_OR_KEYWORD }) patch_signature.return_value = func_sigs_fake inst = VQE(minimizer) t_result = inst.vqe_run(param_prog, hamiltonian, initial_param, qc=fake_qvm) assert np.isclose(t_result.fun, 1.0)
def test_expectation(): """ expectation() routine can take a PauliSum operator on a matrix. Check this functionality and the return of the correct scalar value. """ X = np.array([[0, 1], [1, 0]]) def RX_gate(phi): return expm(-1j * phi * X) def rotation_wavefunction(phi): state = np.array([[1], [0]]) return RX_gate(phi).dot(state) prog = Program([RX(-2.5, 0)]) hamiltonian = PauliTerm("Z", 0, 1.0) minimizer = MagicMock() fake_result = Mock() fake_result.fun = 1.0 minimizer.return_value = fake_result with patch("pyquil.api.QuantumComputer") as fake_qc, \ patch("grove.pyvqe.vqe.WavefunctionSimulator") as mock_wfs: fake_qc.run.return_value = [[0], [0]] fake_wfs = Mock(WavefunctionSimulator) fake_wfs.wavefunction.return_value = Wavefunction( rotation_wavefunction(-2.5)) fake_wfs.expectation.return_value = [0.28366219] mock_wfs.return_value = fake_wfs inst = VQE(minimizer) energy = inst.expectation(prog, PauliSum([hamiltonian]), None, fake_qc) assert np.isclose(energy, 0.28366219) hamiltonian = np.array([[1, 0], [0, -1]]) energy = inst.expectation(prog, hamiltonian, None, fake_qc) assert np.isclose(energy, 0.28366219) prog = Program(H(0)) hamiltonian = PauliSum([PauliTerm('X', 0)]) energy = inst.expectation(prog, hamiltonian, 2, fake_qc) assert np.isclose(energy, 1.0)
def run_with_qc(qc, angle_range): def small_ansatz(params): return Program(RX(params[0], 0)) hamiltonian = sZ(0) vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) # Do not run with samples=None otherwise VQE will use WaveFunctionSimulator and will bypass our patch version of qc data = [ vqe_inst.expectation(small_ansatz([angle]), hamiltonian, qc=qc, samples=10000) for angle in angle_range ] return data
def expectation(p1, p2, p3, multi=1): """ Return UCC expectation value for a specified geometry * First runs a psi4 ccsd calculation to get single and double amplitudes to use as ansatz for UCC * Generates a Hamiltonian for the specified geometry * Obtains expectation value using VQE """ geometry = [['O', p1], ['H', p2], ['H', p3]] molecule = MolecularData(geometry, basis='sto-3g', multiplicity=multi, description=str(round(rad, 2)) + "_" + str(round(ang, 2))) # Run Psi4. molecule = run_psi4(molecule, run_ccsd=1, run_fci=1) # Print out some results of calculation. print('\nRAD: {}, ANG: {}\n'.format(rad, ang)) print('FCI energy: {} Hartree.'.format(molecule.fci_energy)) singles_initial = molecule.ccsd_single_amps.flatten() doubles_initial = molecule.ccsd_double_amps.flatten() amps = np.concatenate((singles_initial, doubles_initial), axis=0) print("Compiling the Hamiltonian...") hamiltonian = jordan_wigner( get_fermion_operator(molecule.get_molecular_hamiltonian())) hamiltonian.compress() hamiltonian = qubitop_to_pyquilpauli(hamiltonian) print("Hamiltonian complete") vqe = VQE(minimizer=minimize, minimizer_kwargs={ 'method': 'nelder-mead', 'options': { 'fatol': 1.5e-3 } }) result = vqe.expectation(ansatz(amps), hamiltonian, None, qvm) print("VQE Expectation Value: {} Hartree".format(result)) return result
def test_expectation(): """expectation() routine can take a PauliSum operator on a matrix. Check this functionality and the return of the correct scalar value""" X = np.array([[0, 1], [1, 0]]) def RX_gate(phi): return expm(-1j * phi * X) def rotation_wavefunction(phi): state = np.array([[1], [0]]) return RX_gate(phi).dot(state) prog = Program([RX(-2.5)(0)]) hamiltonian = PauliTerm("Z", 0, 1.0) minimizer = MagicMock() fake_result = Mock() fake_result.fun = 1.0 minimizer.return_value = fake_result fake_qvm = Mock(spec=['wavefunction', 'expectation', 'run']) fake_qvm.wavefunction.return_value = (Wavefunction( rotation_wavefunction(-2.5)), [0]) fake_qvm.expectation.return_value = [0.28366219] # for testing expectation fake_qvm.run.return_value = [[0], [0]] inst = VQE(minimizer) energy = inst.expectation(prog, PauliSum([hamiltonian]), None, fake_qvm) assert np.isclose(energy, 0.28366219) hamiltonian = np.array([[1, 0], [0, -1]]) energy = inst.expectation(prog, hamiltonian, None, fake_qvm) assert np.isclose(energy, 0.28366219) prog = Program(H(0)) hamiltonian = PauliSum([PauliTerm('X', 0)]) energy = inst.expectation(prog, hamiltonian, 2, fake_qvm) assert np.isclose(energy, 1.0)
class qRBM: """ Quantum Classical Hybrid RBM implementation. """ def __init__(self, QM, num_visible, num_hidden, n_quantum_measurements=None, verbose=False): """ create an RBM with the specified number of visible and hidden units Params ------------------------------------------------------------- QM: (rigetti QVM connection) QVM connection for which to use for quantum circuit simulation num_visible: (int) Number of visible units in RBM num_hidden (int) Number of hidden units in RBM n_quantum_measurements: (int) Number of measuremants to use for Quantum expectation estimation (default to None which does analytical) verbose: (bool) Verbosity of qRBM -------------------------------------------------------------- """ self.n_visible = num_visible self.n_hidden = num_hidden self.qvm = QM self.verbose = verbose self.n_quantum_measurements = n_quantum_measurements #tweak at your leisure. self.n_qaoa_steps = 1 self.beta_temp = 2.0 #don't tweak below here unless you know what you're doing. # only want this for built in expectation calculations... self.vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) self.state_prep_angle = np.arctan(np.e**(-self.beta_temp / 2.0)) * 2.0 self.WEIGHTS = np.asarray( np.random.uniform( low=-0.1 * np.sqrt(6. / (num_hidden + num_visible)), high=0.1 * np.sqrt(6. / (num_hidden + num_visible)), size=(num_visible, num_hidden))) # IN THIS VERSION BIAS IS UNUSED! self.BIAS = np.asarray( np.random.uniform( low=-0.1 * np.sqrt(6. / (num_hidden + num_visible)), high=0.1 * np.sqrt(6. / (num_hidden + num_visible)), size=(num_hidden))) #BIASES ARE ON HIDDENS. # W[i][j] = ith visible to jth hidden # Bias[j] = bias on jth hidden. def make_unclamped_QAOA(self): """ Internal helper function for building QAOA circuit to get RBM expectation using Rigetti Quantum simulator Returns --------------------------------------------------- nus: (list) optimal parameters for cost hamiltonians in each layer of QAOA gammas: (list) optimal parameters for mixer hamiltonians in each layer of QAOA para_prog: (fxn closure) fxn to return QAOA circuit for any supplied nus and gammas --------------------------------------------------- """ visible_indices = [i for i in range(0, self.n_visible)] hidden_indices = [i + self.n_visible for i in range(0, self.n_hidden)] full_cost_operator = [] full_mixer_operator = [] for i in visible_indices: for j in hidden_indices: full_cost_operator.append( PauliSum([ PauliTerm("Z", i, -1.0 * self.WEIGHTS[i][j - self.n_visible]) * PauliTerm("Z", j, 1.0) ])) # UNCOMMENT THIS TO ADD BIAS IN *untested* in this version of code* # for i in hidden_indices: # full_cost_operator.append(PauliSum([PauliTerm("Z", i, -1.0 * self.BIAS[i - self.n_visible])])) for i in hidden_indices + visible_indices: full_mixer_operator.append(PauliSum([PauliTerm("X", i, 1.0)])) n_system = len(visible_indices) + len(hidden_indices) state_prep = pq.Program() for i in visible_indices + hidden_indices: tmp = pq.Program() tmp.inst(RX(self.state_prep_angle, i + n_system), CNOT(i + n_system, i)) state_prep += tmp full_QAOA = QAOA(self.qvm, n_qubits=n_system, steps=self.n_qaoa_steps, ref_hamiltonian=full_mixer_operator, cost_ham=full_cost_operator, driver_ref=state_prep, store_basis=True, minimizer=fmin_bfgs, minimizer_kwargs={'maxiter': 50}, vqe_args={'samples': self.n_quantum_measurements}, rand_seed=1234) nus, gammas = full_QAOA.get_angles() if self.verbose: print 'Found following for nus and gammas from QAOA' print nus print gammas print '-' * 80 program = full_QAOA.get_parameterized_program() return nus, gammas, program, 0 #full_QAOA.result['fun'] def sigmoid(self, x): """ simple helper function to compute sigmoid across data matrix where the rows are samples Params ----------------- DATA: (array) matrix of data where rows are samples ----------------- Returns ----------------- result: (array) that same matrix but with sigmoid applied to entries ----------------- """ return 1.0 / (1.0 + np.exp(-x)) def train(self, DATA, learning_rate=0.1, n_epochs=100, quantum_percentage=1.0, classical_percentage=0.0): """ Train an RBM with mixture of quantum and classical update rules Params ------------------------------------------------------------------------- DATA: (list) matrix with rows as data samples learning_rate: (float) the learning rate used in the update rule by the rbm good value is 0.1 n_epochs: (int) number of weight update loops to do over RBM weights quantum_percentage: (float) fraction of update rule to be dictated by quantum circuit classical_percentage: (float) fraction of update rule to be dictated by classical CD-1 -------------------------------------------------------------------------- NOTE: quantum_percentage + classical_percentage =1.0 must hold!!! """ assert (quantum_percentage + classical_percentage == 1.0) DATA = np.asarray(DATA) for epoch in range(n_epochs): print 'Beginning epoch', epoch visible_indices = [i for i in range(0, self.n_visible)] hidden_indices = [ i + self.n_visible for i in range(0, self.n_hidden) ] new_weights = copy.deepcopy(self.WEIGHTS) new_bias = copy.deepcopy(self.BIAS) model_nus, model_gammas, model_para_prog, _ = self.make_unclamped_QAOA( ) model_sampling_prog = model_para_prog( np.hstack((model_nus, model_gammas))) print 'Found model expectation program....' neg_phase_quantum = np.zeros_like(self.WEIGHTS) # UNCOMMENT FOR BIAS # neg_phase_quantum_bias = np.zeros_like(self.BIAS) for a in range(self.n_visible): for b in range(self.n_hidden): model_expectation = self.vqe_inst.expectation( model_sampling_prog, sZ(visible_indices[a]) * sZ(hidden_indices[b]), self.n_quantum_measurements, self.qvm) neg_phase_quantum[a][b] = model_expectation # UNCOMMENT THIS FOR NEGATIVE PHASE COMPONENT OF BIAS # for b in range(self.n_hidden): # model_expectation = self.vqe_inst.expectation(model_sampling_prog, # sZ(hidden_indices[b]), # n_measurements, # self.qvm) # neg_phase_quantum_bias[b] = model_expectation #IF ADDING BIAS MODIFY THIS AS WELL!!! #follow all standard conventions... hidden_probs = self.sigmoid(np.dot(DATA, self.WEIGHTS)) pos_phase = np.dot(DATA.T, hidden_probs) * (1. / float(len(DATA))) pos_hidden_states = hidden_probs > np.random.rand( len(DATA), self.n_hidden) neg_visible_activations = np.dot(pos_hidden_states, self.WEIGHTS.T) neg_visible_probs = self.sigmoid(neg_visible_activations) neg_hidden_activations = np.dot(neg_visible_probs, self.WEIGHTS) neg_hidden_probs = self.sigmoid(neg_hidden_activations) neg_phase_classical = np.dot(neg_visible_probs.T, neg_hidden_probs) * 1. / len(DATA) if self.verbose: print 'POSITIVE PHASE,' print pos_phase print 'NEGATIVE PHASE (QUANTUM)' print neg_phase_quantum print 'Negative PHASE(classical)' print neg_phase_classical print 'WEIGHTS' print self.WEIGHTS print '-' * 80 # can update weights with weighted avg of quantum and classical. new_weights += learning_rate * ( pos_phase - (classical_percentage * neg_phase_classical + quantum_percentage * neg_phase_quantum)) # UNCOMMENT HERE TO DO BIAS UPDATES #can update bias with weighted avg of quantum and classical. # new_bias += learning_rate * (pos_expect_bias - (0.0*neg_associations_bias + 1.0 * neg_phase_quantum_bias)) self.WEIGHTS = copy.deepcopy(new_weights) self.BIAS = copy.deepcopy(new_bias) with open("RBM_info.txt", "w") as myfile: myfile.write(json.dumps(list(self.WEIGHTS.tolist())) + '\n') myfile.write(json.dumps(list(self.BIAS.tolist())) + '\n') with open("RBM_history.txt", "a") as myfile: myfile.write(json.dumps(list(self.WEIGHTS.tolist())) + '\n') myfile.write(json.dumps(list(self.BIAS.tolist())) + '\n') myfile.write(str('-' * 80) + '\n') print 'Training Done!' def transform(self, DATA): """ Transforms vectors from visible to hidden Params ----------------- DATA: (list) matrix containing rows that are data samples ----------------- Returns ---------------- result: (list) the hidden layers invoked from the samples in data matrix ---------------- """ # MODIFY THIS IF INCLUDING BIAS return self.sigmoid(np.dot(DATA, self.WEIGHTS))
molecule = MolecularData(geometry, basis, spin, description="h3") molecule = run_pyscf(molecule, run_scf=run_scf, run_mp2=run_mp2, run_cisd=run_cisd, run_ccsd=run_ccsd, run_fci=run_fci) # Use a Jordan-Wigner encoding, and compress to remove 0 imaginary components qubit_hamiltonian = jordan_wigner(molecule.get_molecular_hamiltonian()) qubit_hamiltonian.compress() pauli_hamiltonian = qubitop_to_pyquilpauli(qubit_hamiltonian) vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) # angle = 2.0 # result = vqe_inst.expectation(small_ansatz([angle]), pauli_hamiltonian, None, qvm) # print(result) angle_range = np.linspace(0.0, 2 * np.pi, 20) data = [ vqe_inst.expectation(small_ansatz([angle]), pauli_hamiltonian, None, qvm) for angle in angle_range ] import matplotlib.pyplot as plt plt.xlabel('Angle [radians]') plt.ylabel('Expectation value') plt.plot(angle_range, data)
class QBM: """ Quantum Classical Hybrid RBM implementation. """ def __init__(self, qvm=None, num_visible=2, num_hidden=1, steps=3, temp=1.0, quant_meas_num=None, bias=False, reduced=False): """ create an RBM with the specified number of visible and hidden units Params ------------------------------------------------------------- qvm: (Rigetti QVM connection) Simulator, num_visible: (int) Number of visible units, num_hidden: (int) Number of hidden units, steps: (int) Number of steps for QAOA, temp: (float) Temperature of the system, quant_meas_num: (int) Number of measuremants to use for Quantum expectation estimation. -------------------------------------------------------------- """ # Initializing the Params self.visible_units = num_visible self.hidden_units = num_hidden self.total_units = self.visible_units + self.hidden_units self.qvm = make_qvm(qvm) self.quant_meas_num = quant_meas_num self.qaoa_steps = steps self.beta_temp = temp self.state_prep_angle = np.arctan(np.exp(-1 / self.beta_temp)) * 2.0 self.vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) self.param_wb = 0.1 * np.sqrt(6. / self.total_units) self.WEIGHTS = np.asarray( np.random.uniform(low=-self.param_wb, high=self.param_wb, size=(num_visible, num_hidden))) # Using Reduced or Full Botlzman machines. if reduced: self.reduced = True else: self.reduced = False # Using Bias or not. if bias: self.BIAS = np.asarray( np.random.uniform(low=-self.param_wb, high=self.param_wb, size=(self.hidden_units))) else: self.BIAS = None def make_unclamped_QAOA(self): """ Internal helper function for building QAOA circuit to get RBM expectation using Rigetti Quantum simulator Returns --------------------------------------------------- nus: (list) optimal parameters for cost hamiltonians in each layer of QAOA gammas: (list) optimal parameters for mixer hamiltonians in each layer of QAOA para_prog: (fxn closure) fxn to return QAOA circuit for any supplied nus and gammas --------------------------------------------------- """ # Indices visible_indices = [i for i in range(self.visible_units)] hidden_indices = [ i + self.visible_units for i in range(self.hidden_units) ] total_indices = [i for i in range(self.total_units)] # Full Mixer and Cost Hamiltonian Operator full_mixer_operator = [] for i in total_indices: full_mixer_operator.append(PauliSum([PauliTerm("X", i, 1.0)])) full_cost_operator = [] for i in visible_indices: for j in hidden_indices: full_cost_operator.append( PauliSum([ PauliTerm( "Z", i, -1.0 * self.WEIGHTS[i][j - self.visible_units]) * PauliTerm("Z", j, 1.0) ])) if self.BIAS is not None: for i in hidden_indices: print(i, self.visible_units, i - self.visible_units, self.BIAS[i - self.visible_units]) full_cost_operator.append( PauliSum([ PauliTerm("Z", i, -1.0 * self.BIAS[i - self.visible_units]) ])) # Prepare all the units in a thermal state of the full mixer hamiltonian. state_prep = pq.Program() for i in total_indices: tmp = pq.Program() tmp.inst(RX(self.state_prep_angle, i + self.total_units), CNOT(i + self.total_units, i)) state_prep += tmp # QAOA on full mixer and full cost hamiltonian evolution full_QAOA = QAOA(self.qvm, qubits=total_indices, steps=self.qaoa_steps, ref_ham=full_mixer_operator, cost_ham=full_cost_operator, driver_ref=state_prep, store_basis=True, minimizer=fmin_bfgs, minimizer_kwargs={'maxiter': 100}, vqe_options={'samples': self.quant_meas_num}, rand_seed=1234) nus, gammas = full_QAOA.get_angles() program = full_QAOA.get_parameterized_program() return nus, gammas, program, 0 def make_clamped_QAOA(self, data_point, iter): """ Internal helper function for building QAOA circuit to get RBM expectation using Rigetti Quantum simulator Returns --------------------------------------------------- nus: (list) optimal parameters for cost hamiltonians in each layer of QAOA gammas: (list) optimal parameters for mixer hamiltonians in each layer of QAOA para_prog: (fxn closure) fxn to return QAOA circuit for any supplied nus and gammas --------------------------------------------------- """ # Indices visible_indices = [i for i in range(self.visible_units)] hidden_indices = [ i + self.visible_units for i in range(self.hidden_units) ] total_indices = [i for i in range(self.total_units)] # Partial Mixer and Partial Cost Hamiltonian partial_mixer_operator = [] for i in hidden_indices: partial_mixer_operator.append(PauliSum([PauliTerm("X", i, 1.0)])) partial_cost_operator = [] for i in visible_indices: for j in hidden_indices: partial_cost_operator.append( PauliSum([ PauliTerm( "Z", i, -1.0 * self.WEIGHTS[i][j - self.visible_units]) * PauliTerm("Z", j, 1.0) ])) if self.BIAS is not None: for i in hidden_indices: partial_cost_operator.append( PauliSum([ PauliTerm("Z", i, -1.0 * self.BIAS[i - self.visible_units]) ])) state_prep = pq.Program() # state_prep = arbitrary_state.create_arbitrary_state(data_point,visible_indices) # Prepare Visible units as computational basis state corresponding to data point. for i, j in enumerate(data_point): #print(i,j) if j == 1: state_prep += X(i) # Prepare Hidden units in a thermal state of the partial mixer hamiltonian. for i in hidden_indices: tmp = pq.Program() tmp.inst(RX(self.state_prep_angle, i + self.total_units), CNOT(i + self.total_units, i)) state_prep += tmp # QAOA on parital mixer and partial cost hamiltonian evolution partial_QAOA = QAOA(qvm=self.qvm, qubits=total_indices, steps=self.qaoa_steps, ref_ham=partial_mixer_operator, cost_ham=partial_cost_operator, driver_ref=state_prep, store_basis=True, minimizer=fmin_bfgs, minimizer_kwargs={'maxiter': 100 // iter}, vqe_options={'samples': self.quant_meas_num}, rand_seed=1234) nus, gammas = partial_QAOA.get_angles() program = partial_QAOA.get_parameterized_program() return nus, gammas, program, 1 def sigmoid(self, x): return 1.0 / (1.0 + np.exp(-x)) def train(self, DATA, learning_rate=0.1, n_epochs=100, quantum_percentage=1.0, classical_percentage=0.0): """ Train an RBM with mixture of quantum and classical update rules Params ------------------------------------------------------------------------- DATA: (list) matrix with rows as data samples learning_rate: (float) the learning rate used in the update rule by the rbm good value is 0.1 n_epochs: (int) number of weight update loops to do over RBM weights quantum_percentage: (float) fraction of update rule to be dictated by quantum circuit classical_percentage: (float) fraction of update rule to be dictated by classical CD-1 -------------------------------------------------------------------------- NOTE: quantum_percentage + classical_percentage =1.0 must hold!!! """ assert (quantum_percentage + classical_percentage == 1.0) DATA = np.asarray(DATA) assert (len(DATA[0]) <= self.visible_units) for epoch in range(n_epochs): print('Epoch: ', epoch) # Indices visible_indices = [i for i in range(self.visible_units)] hidden_indices = [ i + self.visible_units for i in range(self.hidden_units) ] total_indices = [i for i in range(self.total_units)] new_weights = deepcopy(self.WEIGHTS) if self.BIAS is not None: new_bias = deepcopy(self.BIAS) unc_nus, unc_gammas, unc_para_prog, _ = self.make_unclamped_QAOA() unc_mod_samp_prog = unc_para_prog(np.hstack((unc_nus, unc_gammas))) print('Found model expectation program') unc_neg_phase_quant = np.zeros_like(self.WEIGHTS) for i in range(self.visible_units): for j in range(self.hidden_units): model_expectation = self.vqe_inst.expectation( unc_mod_samp_prog, sZ(visible_indices[i]) * sZ(hidden_indices[j]), self.quant_meas_num, self.qvm) unc_neg_phase_quant[i][j] = model_expectation unc_neg_phase_quant *= (1. / float(len(DATA))) if self.BIAS is not None: unc_neg_phase_quant_bias = np.zeros_like(self.BIAS) for i in range(self.hidden_units): model_expectation = self.vqe_inst.expectation( unc_mod_samp_prog, sZ(hidden_indices[i]), self.quant_meas_num, self.qvm) unc_neg_phase_quant_bias[i] = model_expectation unc_neg_phase_quant_bias *= (1. / float(len(DATA))) pos_hidden_probs = self.sigmoid(np.dot(DATA, self.WEIGHTS)) pos_hidden_states = pos_hidden_probs > np.random.rand( len(DATA), self.hidden_units) pos_phase_classical = np.dot(DATA.T, pos_hidden_probs) * 1. / len(DATA) c_pos_phase_quant = np.zeros_like(self.WEIGHTS) if self.BIAS is not None: c_pos_phase_quant_bias = np.zeros_like(self.BIAS) if not self.reduced: iter_dat = len(DATA) for data in DATA: c_nus, c_gammas, c_para_prog, _ = self.make_clamped_QAOA( data_point=data, iter=iter_dat) c_mod_samp_prog = c_para_prog(np.hstack((c_nus, c_gammas))) print('Found model expectation program') ct_pos_phase_quant = np.zeros_like(self.WEIGHTS) for i in range(self.visible_units): for j in range(self.hidden_units): model_expectation = self.vqe_inst.expectation( c_mod_samp_prog, sZ(visible_indices[i]) * sZ(hidden_indices[j]), self.quant_meas_num, self.qvm) ct_pos_phase_quant[i][j] = model_expectation c_pos_phase_quant += ct_pos_phase_quant if self.BIAS is not None: ct_pos_phase_quant_bias = np.zeros_like(self.BIAS) for i in range(self.hidden_units): model_expectation = self.vqe_inst.expectation( c_mod_samp_prog, sZ(hidden_indices[j]), self.quant_meas_num, self.qvm) ct_pos_phase_quant_bias[i] = model_expectation c_pos_phase_quant_bias += ct_pos_phase_quant_bias c_pos_phase_quant *= (1. / float(len(DATA))) if self.BIAS is not None: c_pos_phase_quant_bias *= (1. / float(len(DATA))) neg_visible_activations = np.dot(pos_hidden_states, self.WEIGHTS.T) neg_visible_probs = self.sigmoid(neg_visible_activations) neg_hidden_activations = np.dot(neg_visible_probs, self.WEIGHTS) neg_hidden_probs = self.sigmoid(neg_hidden_activations) neg_phase_classical = np.dot(neg_visible_probs.T, neg_hidden_probs) * 1. / len(DATA) new_weights += learning_rate * \ (classical_percentage * (pos_phase_classical - neg_phase_classical) + \ quantum_percentage * (c_pos_phase_quant - unc_neg_phase_quant)) print(self.BIAS) ''' if self.BIAS is not None: new_bias = new_bias + learning_rate * \ (classical_percentage * (pos_phase_classical - neg_phase_classical) + \ quantum_percentage * (c_pos_phase_quant_bias - unc_neg_phase_quant_bias)) ''' self.WEIGHTS = deepcopy(new_weights) if self.BIAS is not None: self.BIAS = deepcopy(new_bias) print(self.BIAS) with open("RBM_info.txt", "w") as f: np.savetxt(f, self.WEIGHTS) if self.BIAS is not None: np.savetxt(f, self.BIAS) with open("RBM_history.txt", "a") as f: np.savetxt(f, self.WEIGHTS) if self.BIAS is not None: np.savetxt(f, self.BIAS) f.write(str('*' * 72) + '\n') print('Training Done!') def transform(self, DATA): return self.sigmoid(np.dot(DATA, self.WEIGHTS))
# Get the Hamiltonian in an active space. molecular_hamiltonian = molecule.get_molecular_hamiltonian( occupied_indices=range(active_space_start), active_indices=range(active_space_start, active_space_stop)) # Map operator to fermions and qubits. fermion_hamiltonian = get_fermion_operator(molecular_hamiltonian) qubit_hamiltonian = jordan_wigner(fermion_hamiltonian) # compress removes 0 entries. qubit_hamiltonian is a qubit_operator qubit_hamiltonian.compress() vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) # # # n_amplitudes = uccsd_singlet_paramsize(molecule.n_qubits, molecule.n_electrons) # initial_amplitudes = [0.01] * n_amplitudes # initial_energy = energy_objective(initial_amplitudes) # # # Run VQE Optimization to find new CCSD parameters # opt_result = minimize(energy_objective, initial_amplitudes, # method="CG", options={'disp':True}) # # opt_energy, opt_amplitudes = opt_result.fun, opt_result.x # #
def ZI(params): zi = PauliSum([ PauliTerm.from_list([('Z', 0), ('I', 1)], coefficient=molecule_coefficients[2]) ]) return VQE.expectation(ansatz(params), zi, None, qvm)
def XX(params): xx = PauliSum([ PauliTerm.from_list([('X', 0), ('X', 1)], coefficient=molecule_coefficients[4]) ]) return VQE.expectation(ansatz(params), xx, None, qvm)
def IZ(params): iz = PauliSum([ PauliTerm.from_list([('I', 0), ('Z', 1)], coefficient=molecule_coefficients[3]) ]) return VQE.expectation(ansatz(params), iz, None, qvm)
]) # Define ansatz n_qubits, depth = 3, 3 from pyquil.gates import RY, CNOT def ansatz(params): p = Program() for i in range(depth): p += CNOT(2, 0) for j in range(n_qubits): p += Program(RY(params[j], j)) return p # Minimize and get approximate of the lowest eigenvalue from grove.pyvqe.vqe import VQE qvm = QVMConnection() vqe = VQE(minimizer=minimize, minimizer_kwargs={ 'method': 'nelder-mead', 'options': { 'xatol': 1.0e-2 } }) np.random.seed(999) initial_params = np.random.uniform(0.0, 2 * np.pi, size=n_qubits) result = vqe.vqe_run(ansatz, H, initial_params, samples=None, qvm=qvm) print(result)
def _expectation(_params): exp = PauliSum([PauliTerm.from_list([('Z', 0)])]) return VQE.expectation(self.ansatz(_params[0], 0), exp, None, self.qvm)
# 1. Calling Libraries from pyquil.quil import Program from pyquil.api import QVMConnection from pyquil.gates import RX from pyquil.paulis import sZ, PauliSum, PauliTerm # Calling Grove Library and optimiser from grove.pyvqe.vqe import VQE import numpy as np from scipy.optimize import minimize # 2. Initialising the program qvm = QVMConnection() vqe = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) # 3. Defining ansatz def ansatz(theta): qp = Program() qp.inst(RX(theta[0], 0)) return qp # 4. Defining Hamiltonian hamiltonian = sZ(0) # 5. Running the VQE initial_angle = [0.0] result = vqe.vqe_run(ansatzv, hamiltonian, initial_angle, None, qvm=qvm) print(result)
# Variational-Quantum-Eigensolver in Grove #============================================================================== # Create connection with QVM qc = get_qc('2q-qvm') # Define matrix from pyquil.paulis import sZ H = sZ(0) # Define ansatz from pyquil.gates import RY def ansatz(params): return Program(RY(params[0], 0)) # Minimize and get approximate of the lowest eigenvalue from grove.pyvqe.vqe import VQE vqe = VQE(minimizer=minimize, minimizer_kwargs={ 'method': 'nelder-mead', 'options': { 'initial_simplex': np.array([[0.0], [0.05]]), 'xatol': 1.0e-2 } }) initial_params = [0.0] result = vqe.vqe_run(ansatz, H, initial_params, samples=10000, qc=qc) print(result)
import copy from scipy.optimize import minimize import numpy as np import math import random gate_noise_probs = [0.001, 0.001, 0.001] meas_noise_probs = [0.001, 0.001, 0.001] #qvm = api.SyncConnection('http://127.0.0.1:5000', gate_noise=gate_noise_probs, measurement_noise=meas_noise_probs) qvm = api.QVMConnection() from grove.pyvqe.vqe import VQE vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) beta = 1.0 state_prep_beta = np.arctan(math.e**(-beta / 2.0)) * 2.0 n_qubits = 8 num_good_qubits = int(n_qubits / 2) n_qaoa = 2 #use None value for "god mode" expectation. # must be greater than number of data samples. n_measurement_samples = None def classical_sample(beta): """
PauliTerm.from_list([("I", 0),("I", 1)], coefficient=-3.8505), PauliTerm.from_list([("I", 0),("X", 1)], coefficient=-0.2288), PauliTerm.from_list([("I", 0),("Z", 1)], coefficient=-1.0466), PauliTerm.from_list([("X", 0),("I", 1)], coefficient=-0.2288), PauliTerm.from_list([("X", 0),("X", 1)], coefficient=0.2613), PauliTerm.from_list([("X", 0),("Z", 1)], coefficient=0.2288), PauliTerm.from_list([("Z", 0),("I", 1)], coefficient=-1.0466), PauliTerm.from_list([("Z", 0),("X", 1)], coefficient=0.2288), PauliTerm.from_list([("Z", 0),("Z", 1)], coefficient=0.2356)]) print(hamiltonian) # Define ansatz n_qubits = 2 depth = 3 def ansatz(params): qp = Program() for i in range(depth): qp.inst(CNOT(1,0)) for j in range(n_qubits): qp.inst(RY(params[j], j)) return qp # Minimize and get approximate of the lowest eigenvalue qvm = QVMConnection() vqe = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead', 'options': {'xatol': 1.0e-2}}) # Initial Parameters ip = np.random.uniform(0.0, 2*np.pi, size=n_qubits) result = vqe.vqe_run(ansatz, hamiltonian, ip, samples=None, qvm=qvm) print(result)
def _expectation(_qubits, _params): exp = PauliSum([PauliTerm.from_list([('Z', _qubits[0]), ('Z', _qubits[1])])]) return VQE.expectation(self.ansatz(_params, self.wires, self.depth), exp, None, self.qvm)
class MoMGrad1QB: """ Simple minitbatched MoMGrad implementation for learning "quantum data". More Details of learning algo in the train() fxn """ def __init__(self, q, rx, ry, rz): """ Constructor function for 1qubit MoMGrad. Build a random 1qubt rotation and then use the train function to learn it. params: ------- q: SyncConnection -> qvm connection to use for everything rx: float -> x pauli param (scaled up by a factor of pi) ry: float -> y pauli param (scaled up by a factor of pi) rz: float -> z pauli param (scaled up by a factor of pi) """ self.qvm = q # LAZY, we only use this for expectation calculations and not actual # VQE stuff. from scipy.optimize import minimize self.vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) # DON"T CHANGE BELOW UNLESS YOU KNOW WHAT YOU"RE DOING! # Initialize the indices and statistics for our Oscillators # Indices that our oscialltor will be simulated on self.oscialltor0 = [0, 1, 2] # Initial mu and sigma values for our oscillator self.oscialltor0_stats = [0.0, 0.95] self.oscialltor1 = [3, 4, 5] self.oscialltor1_stats = [0.0, 0.95] self.oscialltor2 = [6, 7, 8] self.oscialltor2_stats = [0.0, 0.95] # Our data qubits for the process we want to learn. self.datum_qubits = [9] # Our chosen learning rate. self.learning_rate = 0.3 # The X,Y,Z params we want to leanr for our abitrary unitary. # Note if learning doesn't go so well there may have been an # internal phase overflow, so be careful.... self.THETA1 = float(rx) # 0.785 self.THETA2 = float(ry) self.THETA3 = float(rz) # 0.906 # Lists to track our learning progress self.param_values = [] self.fid_values = [] # Number of samples to draw for each iteration of learning. self.n_samples = 400 def make_final_qft_inv(self): """ Helper function to give us the F^-1 matrix for our (qubit simulated) harmonic oscillators """ d = 7. indices = [-3, -2, -1, 0, 1, 2, 3] ret = np.identity(int(d), dtype=complex) def w_if(i, j): omega = np.e**(((2.0 * np.pi) * (0.0 + 1.0j)) / d) return omega**(1.0 * (indices[i] * indices[j])) for i in range(len(indices)): for j in range(len(indices)): ret[i][j] = w_if(i, j) ret = block_diag(ret, np.asarray([[np.sqrt(d)]])) ret = ret * 1. / np.sqrt(d) return ret def make_final_qft(self): """ Same as above but makes F """ U = make_final_qft_inv() return np.conj(U) def make_oscillator_gate(self, psi): """ Gives a matrix to prepare an oscillator that has a given distribution specified by the coefficients in psi basically uses 1 iteration of shor (kinda) params: ------- psi: list -> oscillator state probabilites returns: ------- U: matrix -> unitary that prepares our oscillator """ psi = np.asarray(psi) zero = np.zeros_like(psi, dtype=complex) zero[0] = 1.0 flag = True for i in range(len(psi)): if psi[i] != zero[i]: flag = False if flag: # psi is zero so return identity return np.identity(len(psi), dtype=complex) z = np.dot(zero, psi) theta = None if z == 0: theta = 0.0 else: theta = np.arctan(z.imag / z.real) e_i_psi = (np.e**((0.0 - 1.0j) * theta)) * psi phi = e_i_psi - zero # WEIRD MINUS SIGN ISSUE !?!?!? U = np.identity(len(psi), dtype=complex) - \ (2.0/np.vdot(phi, phi)) * np.outer(phi, np.conj(phi)) U = (np.e**((0.0 + 1.0j) * theta)) * U return U def prep_oscillator(self, mu, sigma): """ Gives probabilities for a 7dim oscillator with given mu and sigma params: ------- mu: float -> center of the distribution for the oscillator sigma: float -> std of the distribution of the oscillator returns: -------- psi: list -> list of state probabilities for the oscillator """ d = 7. psi = [0 for i in range(int(d))] for i in range(len(psi)): big = np.e**((-(float(i) - (d - 1.) / 2. - mu)**2.) / (4.0 * sigma**2.)) psi[i] = big psi.append(0) psi = np.asarray(psi) denom = 0.0 for i in range(len(psi)): denom += np.absolute(psi[i])**2. if denom != 0: psi = psi * 1. / np.sqrt(denom) return psi def get_measure_ham(self, indices): """ Helper function so we can properly measure our simulated oscillators params: ------- indices: list of length 3 -> qubit indices that make up an oscillator returns: -------- overall_final: PauliSum -> hacky hamiltonian of paulis for taking expectation values later """ q0 = indices[0] q1 = indices[1] q2 = indices[2] overall_final = ZERO() overall_final += -0.5 * sI(q0) * sZ(q1) * sI(q2) overall_final += -0.5 * sI(q0) * sZ(q1) * sZ(q2) overall_final += -1.5 * sZ(q0) * sI(q1) * sI(q2) overall_final += -0.5 * sZ(q0) * sI(q1) * sZ(q2) overall_final += -0.5 * sZ(q0) * sZ(q1) * sI(q2) overall_final += 0.5 * sZ(q0) * sZ(q1) * sZ(q2) overall_final = overall_final.simplify() # print overall_final.simplify() return overall_final def make_bottom_phase_gate(self, lr, output_state): """ Helper function to make the 'custom gate' corresponding to phase kicking the cost params: ------- lr: float -> learning rate for our algorithm output_state: list -> output state to use in calculating cost returns: -------- U: matrix -> unitary orresponding to gate """ U = np.identity(2, dtype=complex) - \ np.outer(output_state, np.conj(output_state)) return expm((0.0 - 1.0j) * lr * U) def apply_para_gate(self, theta1, theta2, theta3): """ Helper function to evaluate our paramterized state params: ------- theta1: float -> X param theta2: float -> Y param theta3: float -> Z param returns: -------- U: matrix -> result of: e^(-i/2 * theta1 * X) * e^(-i/2 * theta2 * Y) * e^(-i/2 * theta3 * Z) """ zexp = expm( (theta3 * (0.0 - 1.0j) / 2.0) * np.matrix([[1., 0.], [0., -1.]])) yexp = expm((theta2 * (0.0 - 1.0j) / 2.0) * np.matrix([[0, 0.0 - 1.0j], [0.0 + 1.0j, 0]])) xexp = expm((theta1 * (0.0 - 1.0j) / 2.0) * np.matrix([[0.0, 1.0], [1.0, 0.0]])) # return np.matmul(np.matmul(xexp, yexp), zexp) return np.matmul(zexp, np.matmul(yexp, xexp)) def fidelity(self, theta1, theta2, theta3): """ Helper function to calculate fidelity between two pure states defined with the paramaterized state above in apply_para_gate params: ------- theta1: float -> X param theta2: float -> Y param theta3: float -> Z param returns: -------- fidelity: float -> | <A | B > | ** 2 """ B = self.apply_para_gate(self.THETA1, self.THETA2, self.THETA3) A = self.apply_para_gate(theta1, theta2, theta3) a1 = np.dot(A, [(1.0 + 0.0j), (0.0 + 0.0j)]) a2 = np.dot(B, [(1.0 + 0.0j), (0.0 + 0.0j)]) return np.absolute(np.dot(a2, np.conj(a1)))**2 def random_point_sample(self): """ Helper function to generate uniform random samples from the surface of a sphere We return the following ( |0>, random Unitary, |random state>, |random state transformed by our black box we want to learn> ) returns: -------- zero_state: list -> bra-ket zero state U: matrix -> haar random matrix to transform zero state initial_rand_state: list -> bra-ket random state to be fed through our black box wwe want to learn transformed state: list -> bra-ket of random state that has been transformed by our goal unitary """ u = random.random() v = random.random() alpha = 2.0 * np.pi * u theta = np.arccos(2.0 * v - 1.0) theta /= 2.0 U = np.matrix([[ np.cos(theta), -1.0 * np.sin(theta) * np.e**((0.0 - 1.0j) * alpha) ], [np.sin(theta) * np.e**((0.0 + 1.0j) * alpha), np.cos(theta)]]) tmp_prog = pq.Program().defgate("random_set", U) # if random.random() < 0.5: # tmp_prog.inst(X(0)) args = ('random_set', ) + tuple([0]) tmp_prog.inst(args) # 0 state coef then 1 state coef. zero_state = np.asarray([1.0, 0.0]) initial_rand_state = self.qvm.wavefunction(tmp_prog).amplitudes tmp_prog = pq.Program().defgate('random_set', U) tmp_prog += pq.Program().defgate( 'para_gate', self.apply_para_gate(self.THETA1, self.THETA2, self.THETA3)) args = ('random_set', ) + tuple([0]) tmp_prog.inst(args) args = ('para_gate', ) + tuple([0]) tmp_prog.inst(args) transformed_state = self.qvm.wavefunction(tmp_prog).amplitudes return zero_state, U, initial_rand_state, transformed_state # oscialltor first qubit index second def make_controlled_pauli(self, pauli_type=None): """ Helper function to create an oscillator controlled pauli gate params: ------- pauli_type: str -> code for which pauli we're gonna make returns: -------- U: matrix -> unitary corresponding to oscillator parameterized pauli """ if pauli_type == None: assert (2 == 3) U = None if pauli_type == 'Z': U = np.matrix([[1., 0.], [0., -1.]]) elif pauli_type == 'Y': U = np.matrix([[0., (0.0 - 1.0j)], [(0.0 + 1.0j), 0.]]) elif pauli_type == 'X': U = np.matrix([[0.0, 1.0], [1.0, 0.0]]) else: assert (2 == 3) def tmp(coef): return expm((0.0 - 1.0j) * (coef / 2.0) * U) return block_diag(tmp(-3.0), tmp(-2.0), tmp(-1.0), tmp(0.0), tmp(1.0), tmp(2.0), tmp(3.0), np.identity(2)) def train(self): """ Main Function for our learning algorithm, showcasing MoMGrad on (very small) Quanum data. We use our oscillator as parameters to learn the pauli coeffcients in the exponent of our true unitary We do this by specifying the true unitary we want to learn in the form e^(-i/2 * theta1 * X) * e^(-i/2 * theta2 * Y) * e^(-i/2 * theta3 * Z) = U (can also be seen as Rx, Ry and Rz parameters) our algorithm will then proceed to draw random 1qubit states (basically just sampling on the surface of a sphere). Next we will send these random states through this U, enact a phase corresponding to our cost function and then "uncompute" U. What remains after this uncompute is performed is the direction and value with which we should update our oscillators. This lets us proceed with standard gradient descent on our oscillators to carry out learning. """ # Here we draw 400 'samples' for our learning algorithm # Note that technically we can create circuits to do sampling in a # more "physically realizeable" fashion , however this is simpler to code # and in essence is the same data_samples = [] for i in range(self.n_samples): data_samples.append(list(self.random_point_sample())) # Begin Learning! for KKKK in range(4): delta = 1 osc0_c = 0. osc1_c = 0. osc2_c = 0. # Loop over our samples for datum in data_samples: # Get our random samples and apply our unitary to it _, prep_U, initial_rand_state, unitary_applied_to_rand = datum # Initialize our oscillators to the learned mu values so far. prep_prog = pq.Program().defgate( 'prep0', self.make_oscillator_gate( self.prep_oscillator(self.oscialltor0_stats[0], self.oscialltor0_stats[1]))) prep_prog += pq.Program().defgate( 'prep1', self.make_oscillator_gate( self.prep_oscillator(self.oscialltor1_stats[0], self.oscialltor1_stats[1]))) prep_prog += pq.Program().defgate( 'prep2', self.make_oscillator_gate( self.prep_oscillator(self.oscialltor2_stats[0], self.oscialltor2_stats[1]))) # prep_prog += pq.Program().defgate('prep_in', prep_U) # Phase kick the update for our oscillator mu's middle_prog = pq.Program().defgate( 'bottom_phase', self.make_bottom_phase_gate(-1.0 * self.learning_rate, unitary_applied_to_rand)) # Paramterized Oscillator pauli gates para_prog = pq.Program().defgate('prep_in', prep_U) para_prog += pq.Program().defgate( 'oscialltor_X', self.make_controlled_pauli('X')) para_prog += pq.Program().defgate( 'oscialltor_Y', self.make_controlled_pauli('Y')) para_prog += pq.Program().defgate( 'oscialltor_Z', self.make_controlled_pauli('Z')) # State prep for qubits and oscillators # args = ('prep_in', ) + tuple(a for a in self.datum_qubits) # prep_prog.inst(args) args = ('prep0', ) + tuple(a for a in self.oscialltor0) prep_prog.inst(args) args = ('prep1', ) + tuple(a for a in self.oscialltor1) prep_prog.inst(args) args = ('prep2', ) + tuple(a for a in self.oscialltor2) prep_prog.inst(args) # Program for Oscillator controlled paulis args = ('prep_in', ) + \ tuple(a for a in self.datum_qubits) para_prog.inst(args) args = ('oscialltor_X', ) + \ tuple(a for a in self.oscialltor0) + \ tuple(a for a in self.datum_qubits) para_prog.inst(args) args = ('oscialltor_Y', ) + \ tuple(a for a in self.oscialltor1) + \ tuple(a for a in self.datum_qubits) para_prog.inst(args) args = ('oscialltor_Z', ) + \ tuple(a for a in self.oscialltor2) + \ tuple(a for a in self.datum_qubits) para_prog.inst(args) # Program for phase kicking args = ('bottom_phase', ) + tuple(a for a in self.datum_qubits) middle_prog.inst(args) # Program for measuring our oscillators. measure_prog = pq.Program().defgate('qftinv', self.make_final_qft_inv()) args = ('qftinv', ) + tuple(a for a in self.oscialltor0) measure_prog.inst(args) args = ('qftinv', ) + tuple(a for a in self.oscialltor1) measure_prog.inst(args) args = ('qftinv', ) + tuple(a for a in self.oscialltor2) measure_prog.inst(args) final_prog = prep_prog + para_prog +\ middle_prog + para_prog.dagger() + \ measure_prog # Keep logs and do update of our oscillators as parameters # Note we are effectively minibatching wiht batch size 20 here. # 21 - 1 = 20 if delta % 21 == 0: delta = 1 self.oscialltor0_stats[0] += osc0_c / 20. self.oscialltor0_stats[0] = min(self.oscialltor0_stats[0], 3.0) self.oscialltor0_stats[0] = max(self.oscialltor0_stats[0], -3.0) self.oscialltor1_stats[0] += osc1_c / 20. self.oscialltor1_stats[0] = min(self.oscialltor1_stats[0], 3.0) self.oscialltor1_stats[0] = max(self.oscialltor1_stats[0], -3.0) self.oscialltor2_stats[0] += osc2_c / 20. self.oscialltor2_stats[0] = min(self.oscialltor2_stats[0], 3.0) self.oscialltor2_stats[0] = max(self.oscialltor2_stats[0], -3.0) self.param_values.append([ self.oscialltor0_stats[0], self.oscialltor1_stats[0], self.oscialltor2_stats[0] ]) self.fid_values.append( self.fidelity(self.oscialltor0_stats[0], self.oscialltor1_stats[0], self.oscialltor2_stats[0])) osc0_c = 0. osc1_c = 0. osc2_c = 0. quantum_soln = self.apply_para_gate( self.oscialltor0_stats[0], self.oscialltor1_stats[0], self.oscialltor2_stats[0]) orig_soln = self.apply_para_gate(self.THETA1, self.THETA2, self.THETA3) # Console logging print 'Oscillator info:' print self.oscialltor0_stats print self.oscialltor1_stats print self.oscialltor2_stats print '-' * 80 print 'True Unitary:' print orig_soln print 'Unitary learned so far:' print quantum_soln else: delta += 1 osc0_c += self.vqe_inst.expectation( final_prog, self.get_measure_ham(self.oscialltor0), None, self.qvm) osc1_c += self.vqe_inst.expectation( final_prog, self.get_measure_ham(self.oscialltor1), None, self.qvm) osc2_c += self.vqe_inst.expectation( final_prog, self.get_measure_ham(self.oscialltor2), None, self.qvm) random.shuffle(data_samples) self.oscialltor0_stats[1] -= 0.1 self.oscialltor1_stats[1] -= 0.1 self.oscialltor2_stats[1] -= 0.1 print 'Done!'
from example2 import molecule from openfermion.transforms import jordan_wigner, get_fermion_operator from forestopenfermion import qubitop_to_pyquilpauli from pyquil.quil import Program from pyquil.gates import X from pyquil.paulis import exponentiate from pyquil.api import QVMConnection from grove.pyvqe.vqe import VQE from scipy.optimize import minimize h2_qubit_hamiltonian = jordan_wigner( get_fermion_operator(molecule.get_molecular_hamiltonian())) pyquil_hamiltonian = qubitop_to_pyquilpauli(h2_qubit_hamiltonian) print(pyquil_hamiltonian) # # electrons_program = Program() # electrons_program.inst([X(0), X(1)]) pyquil_program = Program() for term in pyquil_hamiltonian.terms: pyquil_program += exponentiate(0.1 * term) print(pyquil_program) qvm = QVMConnection() wf = qvm.wavefunction(pyquil_program) print(wf) vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'}) result = vqe_inst.expectation(pyquil_program, pyquil_hamiltonian, None, qvm) print(result)