示例#1
0
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)
示例#2
0
文件: qaoa.py 项目: ProteinQure/grove
    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
示例#3
0
    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
示例#6
0
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)
示例#7
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)
示例#8
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
示例#9
0
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
示例#10
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

    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)
示例#11
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))
示例#12
0
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))
示例#14
0
# 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
#
#
示例#15
0
 def ZI(params):
     zi = PauliSum([
         PauliTerm.from_list([('Z', 0), ('I', 1)],
                             coefficient=molecule_coefficients[2])
     ])
     return VQE.expectation(ansatz(params), zi, None, qvm)
示例#16
0
 def XX(params):
     xx = PauliSum([
         PauliTerm.from_list([('X', 0), ('X', 1)],
                             coefficient=molecule_coefficients[4])
     ])
     return VQE.expectation(ansatz(params), xx, None, qvm)
示例#17
0
 def IZ(params):
     iz = PauliSum([
         PauliTerm.from_list([('I', 0), ('Z', 1)],
                             coefficient=molecule_coefficients[3])
     ])
     return VQE.expectation(ansatz(params), iz, None, qvm)
示例#18
0
])

# 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)
示例#19
0
 def _expectation(_params):
     exp = PauliSum([PauliTerm.from_list([('Z', 0)])])
     return VQE.expectation(self.ansatz(_params[0], 0), exp, None,
                            self.qvm)
示例#20
0
# 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)
示例#21
0
# 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)
示例#22
0
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)
示例#24
0
 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)
示例#25
0
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!'
示例#26
0
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)