pyquil.simulation.tools module¶
Miscellaneous tools that are helpful for simulation.
- pyquil.simulation.tools.all_bitstrings(n_bits: int) ndarray [source]¶
All bitstrings in lexicographical order as a 2d np.ndarray.
This should be the same as
np.array(list(itertools.product([0,1], repeat=n_bits)))
but faster.
- pyquil.simulation.tools.lifted_gate(gate: Gate, n_qubits: int) ndarray [source]¶
Lift a pyquil
Gate
in a fulln_qubits
-qubit Hilbert space.This function looks up the matrix form of the gate and then dispatches to
lifted_gate_matrix()
with the target qubits.- Parameters:
gate – A gate
n_qubits – The total number of qubits.
- Returns:
A 2^n by 2^n lifted version of the gate acting on its specified qubits.
- pyquil.simulation.tools.lifted_gate_matrix(matrix: ndarray, qubit_inds: Sequence[int], n_qubits: int) ndarray [source]¶
Lift a unitary matrix to act on the specified qubits in a full
n_qubits
-qubit Hilbert space.For 1-qubit gates, this is easy and can be achieved with appropriate kronning of identity matrices. For 2-qubit gates acting on adjacent qubit indices, it is also easy. However, for a multiqubit gate acting on non-adjactent qubit indices, we must first apply a permutation matrix to make the qubits adjacent and then apply the inverse permutation.
- Parameters:
matrix – A 2^k by 2^k matrix encoding an n-qubit operation, where
k == len(qubit_inds)
qubit_inds – The qubit indices we wish the matrix to act on.
n_qubits – The total number of qubits.
- Returns:
A 2^n by 2^n lifted version of the unitary matrix acting on the specified qubits.
- pyquil.simulation.tools.lifted_pauli(pauli_sum: PauliSum | PauliTerm, qubits: list[int]) ndarray [source]¶
Return a matrix corresponding to the tensor representation of the given PauliSum and qubits.
Useful for generating the full Hamiltonian after a particular fermion to pauli transformation. For example:
Converting a PauliSum X0Y1 + Y1X0 into the matrix
[ [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 - 2.0j], [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], [0.0 + 2.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j], ]
Developer note: Quil and the QVM like qubits to be ordered such that qubit 0 is on the right. Therefore, in
qubit_adjacent_lifted_gate
,lifted_pauli
, andlifted_state_operator
, we build up the lifted matrix by performing the kronecker product from right to left.- Parameters:
pauli_sum – Pauli representation of an operator
qubits – list of qubits in the order they will be represented in the resultant matrix.
- Returns:
matrix representation of the pauli_sum operator
- pyquil.simulation.tools.lifted_state_operator(state: TensorProductState, qubits: list[int]) ndarray [source]¶
Return a matrix corresponding to the tensored-up representation of the given state and qubits.
Developer note: Quil and the QVM like qubits to be ordered such that qubit 0 is on the right. Therefore, in
qubit_adjacent_lifted_gate
,lifted_pauli
, andlifted_state_operator
, we build up the lifted matrix by using the left kronecker product.- Parameters:
state – The state
qubits – list of qubits in the order they will be represented in the resultant matrix.
- pyquil.simulation.tools.permutation_arbitrary(qubit_inds: Sequence[int], n_qubits: int) tuple[ndarray, ndarray, int] [source]¶
Generate the permutation matrix that permutes an arbitrary number of single-particle Hilbert spaces into adjacent positions.
Transposes the qubit indices in the order they are passed to a contiguous region in the complete Hilbert space, in increasing qubit index order (preserving the order they are passed in).
Gates are usually defined as GATE 0 1 2, with such an argument ordering dictating the layout of the matrix corresponding to GATE. If such an instruction is given, actual qubits (0, 1, 2) need to be swapped into the positions (2, 1, 0), because the lifting operation taking the 8 x 8 matrix of GATE is done in the little-endian (reverse) addressed qubit space.
For example, suppose I have a Quil command CCNOT 20 15 10. The median of the qubit indices is 15 - hence, we permute qubits [20, 15, 10] into the final map [16, 15, 14] to minimize the number of swaps needed, and so we can directly operate with the final CCNOT, when lifted from indices [16, 15, 14] to the complete Hilbert space.
Notes: assumes qubit indices are unique (assured in parent call).
See documentation for further details and explanation.
Done in preparation for arbitrary gate application on adjacent qubits.
- Parameters:
qubit_inds – Qubit indices in the order the gate is applied to.
n_qubits – Number of qubits in system
- Returns:
perm - permutation matrix providing the desired qubit reordering qubit_arr - new indexing of qubits presented in left to right decreasing index order. start_i - starting index to lift gate from
- pyquil.simulation.tools.program_unitary(program: Program, n_qubits: int) ndarray [source]¶
Return the unitary of a pyQuil program.
- Parameters:
program – A program consisting only of
Gate
.:- Returns:
a unitary corresponding to the composition of the program’s gates.
- pyquil.simulation.tools.qubit_adjacent_lifted_gate(i: int, matrix: ndarray, n_qubits: int) ndarray [source]¶
Lift k-qubit gate on adjacent qubits from qubit i to complete Hilbert space of dimension 2 ** num_qubits.
Ex: 1-qubit gate, lifts from qubit i Ex: 2-qubit gate, lifts from qubits (i+1, i) Ex: 3-qubit gate, lifts from qubits (i+2, i+1, i), operating in that order
In general, this takes a k-qubit gate (2D matrix 2^k x 2^k) and lifts it to the complete Hilbert space of dim 2^num_qubits, as defined by the right-to-left tensor product (1) in arXiv:1608.03355.
Developer note: Quil and the QVM like qubits to be ordered such that qubit 0 is on the right. Therefore, in
qubit_adjacent_lifted_gate
,lifted_pauli
, andlifted_state_operator
, we build up the lifted matrix by performing the kronecker product from right to left.Note that while the qubits are addressed in decreasing order, starting with num_qubit - 1 on the left and ending with qubit 0 on the right (in a little-endian fashion), gates are still lifted to apply on qubits in increasing index (right-to-left) order.
- Parameters:
i – starting qubit to lift matrix from (incr. index order)
matrix – the matrix to be lifted
n_qubits – number of overall qubits present in space
- Returns:
matrix representation of operator acting on the complete Hilbert space of all num_qubits.
- pyquil.simulation.tools.scale_out_phase(unitary1: ndarray, unitary2: ndarray) ndarray [source]¶
Return a matrix m equal to unitary1/θ where ɑ satisfies unitary2 = e^(iθ)·unitary1.
- Parameters:
unitary1 – The unitary matrix from which the constant of proportionality should be scaled-out.
unitary2 – The reference matrix.
- Returns:
A matrix (same shape as the input matrices) with the constant of proportionality scaled-out.
- pyquil.simulation.tools.tensor_up(pauli_sum: PauliSum | PauliTerm, qubits: list[int]) ndarray [source]¶
Return a matrix corresponding to the tensor representation of the given PauliSum and qubits.
This is the same as
lifted_pauli()
. Nick R originally wrote this functionality and really likes the nametensor_up
. Who can blame him?- Parameters:
pauli_sum – Pauli representation of an operator
qubits – list of qubits in the order they will be represented in the resultant matrix.
- Returns:
matrix representation of the pauli_sum operator
- pyquil.simulation.tools.two_swap_helper(j: int, k: int, num_qubits: int, qubit_map: ndarray) tuple[ndarray, ndarray] [source]¶
Generate the permutation matrix that permutes two single-particle Hilbert spaces into adjacent positions.
ALWAYS swaps j TO k. Recall that Hilbert spaces are ordered in decreasing qubit index order. Hence, j > k implies that j is to the left of k.
- End results:
j == k: nothing happens j > k: Swap j right to k, until j at ind (k) and k at ind (k+1). j < k: Swap j left to k, until j at ind (k) and k at ind (k-1).
Done in preparation for arbitrary 2-qubit gate application on ADJACENT qubits.
- Parameters:
j – starting qubit index
k – ending qubit index
num_qubits – number of qubits in Hilbert space
qubit_map – current index mapping of qubits
- Returns:
tuple of swap matrix for the specified permutation, and the new qubit_map, after permutation is made