Code Documentation

vqe

VQEexperiment

class vqe.VQEexperiment(qc: Optional[pyquil.api._quantum_computer.QuantumComputer] = None, hamiltonian: Union[pyquil.paulis.PauliSum, List[pyquil.paulis.PauliTerm], None] = None, molecule: openfermion.hamiltonians._molecular_data.MolecularData = None, method: str = 'Numpy', strategy: str = 'UCCSD', optimizer: str = 'BFGS', maxiter: int = 100000000, shotN: int = 10000, active_reset: bool = True, tomography: bool = False, verbose: bool = False, parametric: bool = False, custom_qubits=None)
__init__(qc: Optional[pyquil.api._quantum_computer.QuantumComputer] = None, hamiltonian: Union[pyquil.paulis.PauliSum, List[pyquil.paulis.PauliTerm], None] = None, molecule: openfermion.hamiltonians._molecular_data.MolecularData = None, method: str = 'Numpy', strategy: str = 'UCCSD', optimizer: str = 'BFGS', maxiter: int = 100000000, shotN: int = 10000, active_reset: bool = True, tomography: bool = False, verbose: bool = False, parametric: bool = False, custom_qubits=None)

VQE experiment class. Initialize an instance of this class to prepare a VQE experiment. One may instantiate this class either based on an OpenFermion MolecularData object (containing a chemistry problem Hamiltonian) or manually suggest a Hamiltonian.

The VQE can run circuits on different virtual or real backends: currently, we support the Rigetti QPU backend, locally running QVM, a WavefunctionSimulator, a NumpyWavefunctionSimulator, a PyQVM. Alternatively, one may run the VQE ansatz unitary directly (not decomposed as a circuit) via direct exponentiation of the unitary ansatz, with the ‘linalg’ method. The different backends do not all support parametric gates (yet), and the user can specify whether or not to use it.

Currently, we support two built-in ansatz strategies and the option of setting your own ansatz circuit. The built-in UCCSD and HF strategies are based on data from MolecularData object and thus require one. For finding the groundstate of a custom Hamiltonian, it is required to manually set an ansatz strategy.

Currently, the only classical optimizer for the VQE is the scipy.optimize.minimize module. This may be straightforwardly extended in future releases, contributions are welcome. This class can be initialized with any algorithm in the scipy class, and the max number of iterations can be specified.

For some QuantumComputer objects, the qubit lattice is not numbered 0..N-1 but has architecture-specific logical labels. These need to be manually read from the lattice topology and specified in the list custom_qubits. On the physical hardware QPU, actively resetting the qubits is supported to speed up the repetition time of VQE.

To debug and during development, set verbose=True to print output details to the console.

Parameters:
  • qc ([QuantumComputer(),None]) – object
  • list(PauliTerm)] hamiltonian ([PauliSum,) – Hamiltonian which one would like to simulate
  • molecule (MolecularData) – OpenFermion Molecule data object. If this is given, the VQE module assumes a chemistry experiment using OpenFermion
  • method (str) – string describing the Backend solver method. current options: {Numpy, WFS, linalg, QC}
  • strategy (str) – string describing circuit VQE strategy. current options: {UCCSD, HF, custom_program}
  • optimizer (str) – classical optimization algorithm, choose from scipy.optimize.minimize options
  • maxiter (int) – max number of iterations
  • shotN (int) – number of shots in the Tomography experiments
  • active_reset (bool) – whether or not to actively reset the qubits (see )
  • tomography (bool) – set to False for access to full wavefunction, set to True for just sampling from it
  • verbose (bool) – set to True for verbose output to the console, for all methods in this class
  • parametric (bool) – set to True to use parametric gate compilation, False to compile a new circuit for every iteration
  • custom_qubits (list()) – list of qubits, i.e. [7,0,1,2] ordering the qubit IDs as they appear on the QPU lattice of the QuantumComputer() object.
compile_tomo_expts()

This method compiles the tomography experiment circuits and prepares them for simulation. Every time the circuits are adjusted, re-compiling the tomography experiments is required to affect the outcome.

get_circuit()

This method returns the reference state preparation circuit followed by the ansatz circuit.

Returns:return the PyQuil program which defines the circuit. Excludes tomography rotations.
Return type:Program
get_exact_gs(hamiltonian=None)

Calculate the exact groundstate energy of the loaded Hamiltonian

Parameters:hamiltonian (PauliSum) – (optional) Hamiltonian of which one would like to calculate the GS energy
Returns:groundstate energy
Return type:float
get_history()

Get historical values of objective_function from the classical optimization algorithm. Note: resets at every start_vqe().

Returns:Historical energy values from the optimizer
Return type:list()
get_qubit_req()

This method computes the number of qubits required to represent the desired Hamiltonian: * assumes all Pauli term indices up to the largest one are in use. * assumes pauli_list has been loaded properly.

Returns:number of qubits required in the circuit, as set by the Hamiltonian terms’ indices.
Return type:int
get_results()

get results from the VQE experiment

Returns:scipy.optimize.minimize result object
Return type:OptimizeResult
objective_function(amps=None)

This function returns the Hamiltonian expectation value over the final circuit output state. If argument packed_amps is given, the circuit will run with those parameters. Otherwise, the initial angles will be used.

Parameters:numpy.ndarray] amps ([list(),) – list of circuit angles to run the objective function over.
Returns:energy estimate
Return type:float
save_program(filename)

this saves the preparation circuit as a quil program which can be parsed with pyquil.parser.parse

Parameters:filename (str) – saves the quil program to this filename.
set_circuit_unitary(unitary)

Sets the circuit unitary for use in the ‘linalg’ method.

Parameters:unitary (numpy.ndarray) – np.ndarray of size [2^N x 2^N] where N is the number of qubits
set_custom_ansatz(prog: pyquil.quil.Program = <pyquil.quil.Program object>)
Parameters:prog (Program()) – set a custom ansatz circuit as a program. All variational angles must be parametric !
set_custom_ref_preparation(prog: pyquil.quil.Program = <pyquil.quil.Program object>)
Parameters:prog (Program()) – set a custom reference state preparation circuit as a program. All variational angles must be parametric.
set_initial_angles(angles: List[T])

User-specify the initial angles for the experiment.

Parameters:angles (list()) – list of angles for the circuit
set_initial_state(psi)

Manually set the complex-valued initial state for the qubits (). Only makes sense for the linalg method

Parameters:psi (numpy.ndarray) – np.ndarray or scipy.sparse array: the complex-valued initial state of the qubits
set_maxiter(maxiter: int)

Set the maximum iteration number for the classical optimizer

Parameters:maxiter (int) – maximum iteration number for the classical optimizer
set_tomo_shots(shotN)

Set the number of shots for the tomography experiment. Warning: requires recompilation of the circuits.

Parameters:shotN (int) – number of tomography repetitions.
start_vqe(theta=None, maxiter: int = 0, options: dict = {})

This method starts the VQE algorithm. User can supply an initial circuit setting, otherwise the stored initial settings are used. the maxiter refers to the scipy optimizer number of iterations (which may well be much less than the number of function calls)

Parameters:
  • theta ([list(),numpy.ndarray]) – list of initial angles for the circuit to start the optimizer in.
  • maxiter (int) – maximum number of iterations.
  • options (Dict) – options for the scipy.minimize classical optimizer
Returns:

scipy.optimize.minimize result object containing convergence details and final energies. See scipy docs

Return type:

OptimizeResult

verbose_output(verbose: bool = True)
Parameters:verbose (bool) – set verbose output to console.

GroupedPauliSetting

class vqe.GroupedPauliSetting(list_gsuit_paulis: List[pyquil.paulis.PauliTerm], qc: pyquil.api._quantum_computer.QuantumComputer, ref_state: pyquil.quil.Program, ansatz: pyquil.quil.Program, shotN: int, parametric_way: bool, n_qubits: int, active_reset: bool = True, cq=None, method='QC', verbose: bool = False)
__init__(list_gsuit_paulis: List[pyquil.paulis.PauliTerm], qc: pyquil.api._quantum_computer.QuantumComputer, ref_state: pyquil.quil.Program, ansatz: pyquil.quil.Program, shotN: int, parametric_way: bool, n_qubits: int, active_reset: bool = True, cq=None, method='QC', verbose: bool = False)

A tomography experiment class for use in VQE. In a real experiment, one only has access to measurements in the Z-basis, giving a result 0 or 1 for each qubit. The Hamiltonian may have terms like sigma_x or sigma_y, for which a basis rotation is required. As quantum mechanics prohibits measurements in multiple bases at once, the Hamiltonian needs to be grouped into commuting Pauli terms and for each set of terms the appropriate basis rotations is applied to each qubits.

A parity_matrix is constructed to keep track of what consequence a qubit measurement of 0 or 1 has as a contribution to the Pauli estimation.

The experiments are constructed as objects with class methods to run and adjust them.

Instantiate using the following parameters:

Parameters:
  • list_gsuit_paulis (list()) – list of Pauli terms which can be measured at the same time (they share a TPB!)
  • qc (QuantumComputer) – QuantumComputer() object which will simulate the terms
  • ref_state (Program) – Program() circuit object which produces the initial reference state (f.ex. Hartree-Fock)
  • ansatz (Program) – Program() circuit object which produces the ansatz (f.ex. UCCSD)
  • shotN (int) – number of shots to run this Setting for
  • parametric_way (bool) – boolean whether to use parametric gates or hard-coded gates
  • n_qubits (int) – total number of qubits used for the program
  • active_reset (bool) – boolean whether or not to actively reset the qubits
  • cq (list()) – list of qubit labels instead of the default [0,1,2,3,…,N-1]
  • method (str) – string describing the computational method from {QC, linalg, WFS, Numpy}
  • verbose (bool) – boolean, whether or not to give verbose output during execution
static construct_parity_matrix(pauli_list, n_qubits)

This method constructs a matrix which is used to evaluate PauliTerm expectation values from an array of bitstrings returned by qc.run(). See run_experiment() how this parity_matrix is used.

Parameters:
  • pauli_list (list(PauliTerm)) – list of PauliTerm() objects which should be measured. Assumes a list of non-empty pauli terms only!
  • n_qubits (int) – number of qubits assumed for the parity matrix
Returns:

returns parity_matrix, 2D numpy-array of dimensions [n_qubits, len(pauli_list)]

Return type:

numpy.ndarray

run_experiment(qc: Optional[pyquil.api._quantum_computer.QuantumComputer], angles=None)

method to run the Tomography experiment for this instance’s setting, repeating for shotN shots.

Parameters:
  • None] qc ([QuantumComputer,) – quantum computer object to run the experiment on, or None in case of WFS/Numpy methods
  • angles (list()) – circuit parameters to feed
Returns:

returns sum of all commuting pauli terms estimations for this experiment

Return type:

float

circuits

circuits.augment_program_with_memory_values(quil_program, memory_map)

This function allocates the classical memory values (gate angles) to a parametric quil program in order to use it on a Numpy-based simulator

Parameters:
  • quil_program (Program) – parametric quil program which would require classical memory allocation
  • memory_map (Dict) – dictionary with as keys the MemoryReference or String descrbing the classical memory, and with items() an array of values for that classical memory
Returns:

quil program with gate angles from memory_map allocated to the (originally parametric) program

Return type:

Program

circuits.exponentiate_commuting_pauli_sum_parametric(pauli_sum, term_dict, memref)

Returns a Program() (NOT A function) that maps all substituent PauliTerms and sums them into a program. NOTE: Use this function with care. Substituent PauliTerms in pauli_sum should commute for this to work correctly!

Parameters:
  • pauli_sum (List) – list of Pauli terms to exponentiate.
  • term_dict (Dict) – Dictionary containing as keys the Pauliterm frozensets, and as values the indices in packed_amplitudes (and in Memoreference pointer, same index!), corresponding to the same index in pauli_sum list
  • memref (MemoryReference) – memory reference which should be inserted to generate the program
Returns:

A program that parametrizes the exponential.

Return type:

Program()

circuits.pauli_meas(idx, op)

Generate gate sequence to measure in the eigenbasis of a Pauli operator, assuming we are only able to measure in the Z eigenbasis. The available operations are the following:

\[ \begin{align}\begin{aligned}\begin{split}'X' = \begin{bmatrix} 0 & \frac{-\pi}{2} \\ \frac{-\pi}{2} & 0 \end{bmatrix}\end{split}\\\begin{split}'Y' = \begin{bmatrix} 0 & \frac{-i\pi}{2} \\ \frac{i\pi}{2} & 0 \end{bmatrix}\end{split}\end{aligned}\end{align} \]

and \('Z' = 'I' = \mathbb{I}\).

Parameters:
  • idx (int) – the qubit index on which the measurement basis rotation is to be performed.
  • op (str) – enumeration (‘X’, ‘Y’, ‘Z’, ‘I’) representing the axis of the given Pauli matrix
Returns:

a pyquil Program representing the Pauli matrix projected onto the Z eigenbasis.

Return type:

Program

circuits.ref_state_preparation_circuit(molecule, ref_type='HF', cq=None)

This function returns a program which prepares a reference state to begin from with a Variational ansatz.

Parameters:
  • molecule (MolecularData) – molecule data object containing information on HF state.
  • ref_type (str) – type of reference state desired
  • cq (list()) – (optional) list of qubit labels if different from standard 0 to N-1 convention
Returns:

pyquil program which prepares the reference state

Return type:

Program

circuits.uccsd_ansatz_circuit(packed_amplitudes, n_orbitals, n_electrons, cq=None)

This function returns a UCCSD variational ansatz with hard-coded gate angles. The number of orbitals specifies the number of qubits, the number of electrons specifies the initial HF reference state which is assumed was prepared. The packed_amplitudes input defines which gate angles to apply for each CC operation. The list cq is an optional list which specifies the qubit label ordering which is to be assumed.

Parameters:
  • packed_amplitudes (list()) – amplitudes t_ij and t_ijkl of the T_1 and T_2 operators of the UCCSD ansatz
  • n_orbitals (int) – number of spatial orbitals
  • n_electrons (int) – number of electrons considered
  • cq (list()) – list of qubit label order
Returns:

circuit which prepares the UCCSD variational ansatz

Return type:

Program

circuits.uccsd_ansatz_circuit_parametric(n_orbitals, n_electrons, cq=None)

This function returns a UCCSD variational ansatz with hard-coded gate angles. The number of orbitals specifies the number of qubits, the number of electrons specifies the initial HF reference state which is assumed was prepared. The list cq is an optional list which specifies the qubit label ordering which is to be assumed.

Parameters:
  • n_orbitals (int) – number of spatial orbitals in the molecule (for building UCCSD singlet generator)
  • n_electrons (int) – number of electrons in the molecule
  • cq (list()) – custom qubits
Returns:

program which prepares the UCCSD \(T_1 + T_2\) propagator with a spin-singlet assumption.

Return type:

Program

utils

utils.pyquilpauli_to_qubitop(pyquil_pauli)

Convert a pyQuil PauliSum to an OpenFermion QubitOperator

Parameters:pyquil_pauli ([PauliTerm, PauliSum]) – pyQuil PauliTerm or PauliSum to convert to an OpenFermion QubitOperator
Returns:a QubitOperator representing the PauliSum or PauliTerm
Return type:QubitOperator
utils.qubitop_to_pyquilpauli(qubit_operator)

Convert an OpenFermion QubitOperator to a PauliSum

Parameters:qubit_operator (QubitOperator) – OpenFermion QubitOperator to convert to a pyquil.PauliSum
Returns:PauliSum representing the qubit operator
Return type:PauliSum
utils.uccsd_singlet_generator_with_indices(n_qubits, n_electrons)

Create a singlet UCCSD generator for a system with n_electrons, but return a list of Fermion operators, for each term in packed_amplitudes, name it a list called generator, instead of a sum called generator. It also returns a list of indices matching each term. This function generates a FermionOperator for a UCCSD generator designed to act on a single reference state consisting of n_qubits spin orbitals and n_electrons electrons, that is a spin singlet operator, meaning it conserves spin.

Parameters:
  • n_qubits (int) – Number of spin-orbitals used to represent the system, which also corresponds to number of qubits in a non-compact map.
  • n_electrons (int) – Number of electrons in the physical system.
Returns:

Generator of the UCCSD operator that builds the UCCSD wavefunction.

Return type:

list(FermionOperator)