def one_and_two_body_interaction_reverse_order(p, q, a, b) -> cirq.OP_TREE: yield rot11(rads=-self.hamiltonian.two_body[p, q] * time).on(a, b) yield Ryxxy(0.5 * self.hamiltonian.one_body[p, q].imag * time).on( a, b) yield Rxxyy(0.5 * self.hamiltonian.one_body[p, q].real * time).on( a, b)
def _ops_from_givens_rotations_circuit_description( qubits: Sequence[cirq.QubitId], circuit_description: Iterable[Iterable[ Union[str, Tuple[int, int, float, float]]]]) -> cirq.OP_TREE: """Yield operations from a Givens rotations circuit obtained from OpenFermion. """ for parallel_ops in circuit_description: for op in parallel_ops: if op == 'pht': yield cirq.X(qubits[-1]) else: i, j, theta, phi = cast(Tuple[int, int, float, float], op) yield Ryxxy(theta).on(qubits[i], qubits[j]) yield cirq.Z(qubits[j]) ** (phi / numpy.pi)
def optimal_givens_decomposition( qubits: Sequence[cirq.Qid], unitary: numpy.ndarray) -> Iterable[cirq.Operation]: r""" Implement a circuit that provides the unitary that is generated by single-particle fermion generators .. math:: U(v) = exp(log(v)_{p,q}(a_{p}^{\dagger}a_{q} - a_{q}^{\dagger}a_{p}) This can be used for implementing an exact single-body basis rotation Args: qubits: Sequence of qubits to apply the operations over. The qubits should be ordered in linear physical order. unitary: """ N = unitary.shape[0] right_rotations = [] left_rotations = [] for i in range(1, N): if i % 2 == 1: for j in range(0, i): # eliminate U[N - j, i - j] by mixing U[N - j, i - j], # U[N - j, i - j - 1] by right multiplication # of a givens rotation matrix in column [i - j, i - j + 1] gmat = givens_matrix_elements(unitary[N - j - 1, i - j - 1], unitary[N - j - 1, i - j - 1 + 1], which='left') right_rotations.append((gmat.T, (i - j - 1, i - j))) givens_rotate(unitary, gmat.conj(), i - j - 1, i - j, which='col') else: for j in range(1, i + 1): # elimination of U[N + j - i, j] by mixing U[N + j - i, j] and # U[N + j - i - 1, j] by left multiplication # of a givens rotation that rotates row space # [N + j - i - 1, N + j - i gmat = givens_matrix_elements(unitary[N + j - i - 1 - 1, j - 1], unitary[N + j - i - 1, j - 1], which='right') left_rotations.append((gmat, (N + j - i - 2, N + j - i - 1))) givens_rotate(unitary, gmat, N + j - i - 2, N + j - i - 1, which='row') new_left_rotations = [] for (left_gmat, (i, j)) in reversed(left_rotations): phase_matrix = numpy.diag([unitary[i, i], unitary[j, j]]) matrix_to_decompose = left_gmat.conj().T.dot(phase_matrix) new_givens_matrix = givens_matrix_elements(matrix_to_decompose[1, 0], matrix_to_decompose[1, 1], which='left') new_phase_matrix = matrix_to_decompose.dot(new_givens_matrix.T) # check if T_{m,n}^{-1}D = D T. # coverage: ignore if not numpy.allclose(new_phase_matrix.dot(new_givens_matrix.conj()), matrix_to_decompose): raise GivensTranspositionError("Failed to shift the phase matrix " "from right to left") # coverage: ignore unitary[i, i], unitary[j, j] = new_phase_matrix[0, 0], new_phase_matrix[1, 1] new_left_rotations.append((new_givens_matrix.conj(), (i, j))) phases = numpy.diag(unitary) rotations = [] ordered_rotations = [] for (gmat, (i, j)) in list(reversed(new_left_rotations)) + list( map(lambda x: (x[0].conj().T, x[1]), reversed(right_rotations))): ordered_rotations.append((gmat, (i, j))) # if this throws the impossible has happened # coverage: ignore if not numpy.isclose(gmat[0, 0].imag, 0.0): raise GivensMatrixError( "Givens matrix does not obey our convention that all elements " "in the first column are real") if not numpy.isclose(gmat[1, 0].imag, 0.0): raise GivensMatrixError( "Givens matrix does not obey our convention that all elements " "in the first column are real") # coverage: ignore theta = numpy.arcsin(numpy.real(gmat[1, 0])) phi = numpy.angle(gmat[1, 1]) rotations.append((i, j, theta, phi)) for op in reversed(rotations): i, j, theta, phi = cast(Tuple[int, int, float, float], op) if not numpy.isclose(phi, 0.0): yield cirq.Z(qubits[j])**(phi / numpy.pi) yield Ryxxy(-theta).on(qubits[i], qubits[j]) for idx, phase in enumerate(phases): yield cirq.Z(qubits[idx])**(numpy.angle(phase) / numpy.pi)