def _slater_basis_change( qubits: Sequence[cirq.QubitId], transformation_matrix: numpy.ndarray, initially_occupied_orbitals: Optional[Sequence[int]]) -> cirq.OP_TREE: n_qubits = len(qubits) if initially_occupied_orbitals is None: decomposition, diagonal = givens_decomposition_square( transformation_matrix) circuit_description = list(reversed(decomposition)) # The initial state is not a computational basis state so the # phases left on the diagonal in the decomposition matter yield (cirq.Rz(rads=numpy.angle(diagonal[j])).on(qubits[j]) for j in range(n_qubits)) else: initially_occupied_orbitals = cast(Sequence[int], initially_occupied_orbitals) transformation_matrix = transformation_matrix[list( initially_occupied_orbitals)] n_occupied = len(initially_occupied_orbitals) # Flip bits so that the first n_occupied are 1 and the rest 0 initially_occupied_orbitals_set = set(initially_occupied_orbitals) yield (cirq.X(qubits[j]) for j in range(n_qubits) if (j < n_occupied) != (j in initially_occupied_orbitals_set)) circuit_description = slater_determinant_preparation_circuit( transformation_matrix) yield _ops_from_givens_rotations_circuit_description( qubits, circuit_description)
def diagonalizing_circuit(self): r"""Get a circuit for a unitary that diagonalizes this Hamiltonian This circuit performs the transformation to a basis in which the Hamiltonian takes the diagonal form .. math:: \sum_{j} \varepsilon_j b^\dagger_j b_j + \text{constant}. Returns ------- circuit_description (list[tuple]): A list of operations describing the circuit. Each operation is a tuple of objects describing elementary operations that can be performed in parallel. Each elementary operation is either the string 'pht' indicating a particle-hole transformation on the last fermionic mode, or a tuple of the form :math:`(i, j, \theta, \varphi)`, indicating a Givens rotation of modes :math:`i` and :math:`j` by angles :math:`\theta` and :math:`\varphi`. """ transformation_matrix = self.diagonalizing_bogoliubov_transform() if self.conserves_particle_number: # The Hamiltonian conserves particle number, so we don't need # to use the most general procedure. decomposition, _ = givens_decomposition_square( transformation_matrix) circuit_description = list(reversed(decomposition)) else: # The Hamiltonian does not conserve particle number, so we # need to use the most general procedure. # Rearrange the transformation matrix because the circuit # generation routine expects it to describe annihilation # operators rather than creation operators. left_block = transformation_matrix[:, :self.n_qubits] right_block = transformation_matrix[:, self.n_qubits:] # Can't use numpy.block because that requires numpy>=1.13.0 new_transformation_matrix = numpy.empty( (self.n_qubits, 2 * self.n_qubits), dtype=complex) new_transformation_matrix[:, :self.n_qubits] = numpy.conjugate( right_block) new_transformation_matrix[:, self.n_qubits:] = numpy.conjugate( left_block) # Get the circuit description decomposition, left_decomposition, _, _ = ( fermionic_gaussian_decomposition(new_transformation_matrix)) # need to use left_diagonal too circuit_description = list(reversed( decomposition + left_decomposition)) return circuit_description
def diagonalizing_circuit(self): """Get a circuit for a unitary that diagonalizes this Hamiltonian This circuit performs the transformation to a basis in which the Hamiltonian takes the diagonal form .. math:: \sum_{j} \\varepsilon_j b^\dagger_j b_j + \\text{constant}. Returns ------- circuit_description (list[tuple]): A list of operations describing the circuit. Each operation is a tuple of objects describing elementary operations that can be performed in parallel. Each elementary operation is either the string 'pht' indicating a particle-hole transformation on the last fermionic mode, or a tuple of the form :math:`(i, j, \\theta, \\varphi)`, indicating a Givens rotation of modes :math:`i` and :math:`j` by angles :math:`\\theta` and :math:`\\varphi`. """ diagonalizing_unitary = self.diagonalizing_bogoliubov_transform() if self.conserves_particle_number: # The Hamiltonian conserves particle number, so we don't need # to use the most general procedure. decomposition, diagonal = givens_decomposition_square( diagonalizing_unitary) circuit_description = list(reversed(decomposition)) else: # The Hamiltonian does not conserve particle number, so we # need to use the most general procedure. # Get the unitary rows which represent the Gaussian unitary gaussian_unitary_matrix = diagonalizing_unitary[self.n_qubits:] # Get the circuit description decomposition, left_decomposition, diagonal, left_diagonal = ( fermionic_gaussian_decomposition(gaussian_unitary_matrix)) # need to use left_diagonal too circuit_description = list( reversed(decomposition + left_decomposition)) return circuit_description
def _rotate_mo(mo_coeff, mo_occ, dx): decompositio = [] dr = pyscf.scf.hf.unpack_uniq_var(dx, mo_occ) u = scipy.linalg.expm(dr) decomposition = givens_decomposition_square(u) return np.dot(mo_coeff, u)