def test_erpa_eom_ham_lih(): filename = os.path.join(DATA_DIRECTORY, "H1-Li1_sto-3g_singlet_1.45.hdf5") molecule = MolecularData(filename=filename) reduced_ham = make_reduced_hamiltonian( molecule.get_molecular_hamiltonian(), molecule.n_electrons) rha_fermion = get_fermion_operator(reduced_ham) permuted_hijkl = np.einsum('ijlk', reduced_ham.two_body_tensor) opdm = np.diag([1] * molecule.n_electrons + [0] * (molecule.n_qubits - molecule.n_electrons)) tpdm = 2 * wedge(opdm, opdm, (1, 1), (1, 1)) rdms = InteractionRDM(opdm, tpdm) dim = 3 # so we don't do the full basis. This would make the test long full_basis = {} # erpa basis. A, B basis in RPA language cnt = 0 # start from 1 to make test shorter for p, q in product(range(1, dim), repeat=2): if p < q: full_basis[(p, q)] = cnt full_basis[(q, p)] = cnt + dim * (dim - 1) // 2 cnt += 1 for rkey in full_basis.keys(): p, q = rkey for ckey in full_basis.keys(): r, s = ckey for sigma, tau in product([0, 1], repeat=2): test = erpa_eom_hamiltonian(permuted_hijkl, tpdm, 2 * q + sigma, 2 * p + sigma, 2 * r + tau, 2 * s + tau).real qp_op = FermionOperator( ((2 * q + sigma, 1), (2 * p + sigma, 0))) rs_op = FermionOperator(((2 * r + tau, 1), (2 * s + tau, 0))) erpa_op = normal_ordered( commutator(qp_op, commutator(rha_fermion, rs_op))) true = rdms.expectation(get_interaction_operator(erpa_op)) assert np.isclose(true, test)
def valdemaro_reconstruction(tpdm, n_electrons): """ Build a 3-RDM by cumulant expansion and setting 3rd cumulant to zero d3 approx = D ^ D ^ D + 3 (2C) ^ D tpdm has normalization (n choose 2) where n is the number of electrons Args: tpdm (np.ndarray): four-tensor representing the two-RDM n_electrons (int): number of electrons in the system Returns: six-tensor reprsenting the three-RDM """ opdm = (2 / (n_electrons - 1)) * np.einsum('ijjk', tpdm) unconnected_tpdm = wedge(opdm, opdm, (1, 1), (1, 1)) unconnected_d3 = wedge(opdm, unconnected_tpdm, (1, 1), (2, 2)) return 3 * wedge(tpdm, opdm, (2, 2), (1, 1)) - 2 * unconnected_d3
def test_h2_rpa(): filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5") molecule = MolecularData(filename=filename) reduced_ham = make_reduced_hamiltonian( molecule.get_molecular_hamiltonian(), molecule.n_electrons) hf_opdm = np.diag([1] * molecule.n_electrons + [0] * (molecule.n_qubits - molecule.n_electrons)) hf_tpdm = 2 * wedge(hf_opdm, hf_opdm, (1, 1), (1, 1)) pos_spectrum, xy_eigvects, basis = singlet_erpa( hf_tpdm, reduced_ham.two_body_tensor) assert np.isclose(pos_spectrum, 0.92926444) # pyscf-rpa value assert isinstance(xy_eigvects, np.ndarray) assert isinstance(basis, dict)
def rdms_from_opdm_aa(self, opdm_aa) -> InteractionRDM: """Generate the RDM from just the alpha-alpha block. Due to symmetry, the beta-beta block is the same, and the other blocks are zero. Args: opdm_aa: The alpha-alpha block of the RDM """ opdm = np.zeros((self.num_qubits, self.num_qubits), dtype=complex) opdm[::2, ::2] = opdm_aa opdm[1::2, 1::2] = opdm_aa tpdm = wedge(opdm, opdm, (1, 1), (1, 1)) rdms = InteractionRDM(opdm, 2 * tpdm) return rdms
def rdms_from_rhf_opdm(self, opdm_aa: np.ndarray) -> InteractionRDM: """ Generate spin-orbital InteractionRDM object from the alpha-spin opdm. Args: opdm_aa: single spin sector of the 1-particle denstiy matrix Returns: InteractionRDM object for full spin-orbital 1-RDM and 2-RDM """ opdm = np.zeros((2 * self.num_orbitals, 2 * self.num_orbitals), dtype=np.complex128) opdm[::2, ::2] = opdm_aa opdm[1::2, 1::2] = opdm_aa tpdm = linalg.wedge(opdm, opdm, (1, 1), (1, 1)) rdms = InteractionRDM(opdm, 2 * tpdm) return rdms
def energy_from_opdm(opdm, constant, one_body_tensor, two_body_tensor): """Evaluate the energy of an opdm assuming the 2-RDM is opdm ^ opdm. Args: opdm: single spin-component of the full spin-orbital opdm. constant: constant shift to the Hamiltonian. Commonly this is the nuclear repulsion energy. one_body_tensor: spatial one-body integrals two_body_tensor: spatial two-body integrals """ spin_opdm = np.kron(opdm, np.eye(2)) spin_tpdm = 2 * wedge(spin_opdm, spin_opdm, (1, 1), (1, 1)) molecular_hamiltonian = generate_hamiltonian( constant=constant, one_body_integrals=one_body_tensor, two_body_integrals=two_body_tensor) rdms = InteractionRDM(spin_opdm, spin_tpdm) return rdms.expectation(molecular_hamiltonian).real
def global_gradient_opdm(self, params: np.ndarray, alpha_opdm: np.ndarray): opdm = np.zeros((self.num_qubits, self.num_qubits), dtype=np.complex128) opdm[::2, ::2] = alpha_opdm opdm[1::2, 1::2] = alpha_opdm tpdm = 2 * wedge(opdm, opdm, (1, 1), (1, 1)) # now go through and generate all the necessary Z, Y, Y_kl matrices kappa_matrix = rhf_params_to_matrix(params, len(self.occ) + len(self.virt), self.occ, self.virt) kappa_matrix_full = np.kron(kappa_matrix, np.eye(2)) w_full, v_full = np.linalg.eigh( -1j * kappa_matrix_full) # so that kappa = i U lambda U^ eigs_scaled_full = get_matrix_of_eigs(w_full) grad = np.zeros(self.nocc * self.nvirt, dtype=np.complex128) kdelta = np.eye(self.num_qubits) # NOW GENERATE ALL TERMS ASSOCIATED WITH THE GRADIENT!!!!!! for p in range(self.nocc * self.nvirt): grad_params = np.zeros_like(params) grad_params[p] = 1 Y = rhf_params_to_matrix(grad_params, len(self.occ) + len(self.virt), self.occ, self.virt) Y_full = np.kron(Y, np.eye(2)) # Now rotate Y into the basis that diagonalizes Z Y_kl_full = v_full.conj().T @ Y_full @ v_full # now rotate # Y_{kl} * (exp(i(l_{k} - l_{l})) - 1) / (i(l_{k} - l_{l})) # into the original basis pre_matrix_full = v_full @ (eigs_scaled_full * Y_kl_full) @ v_full.conj().T grad_expectation = -1.0 * np.einsum( 'ab,pq,aq,pb', self.hamiltonian.one_body_tensor, pre_matrix_full, kdelta, opdm, optimize='optimal').real grad_expectation += 1.0 * np.einsum( 'ab,pq,bp,aq', self.hamiltonian.one_body_tensor, pre_matrix_full, kdelta, opdm, optimize='optimal').real grad_expectation += 1.0 * np.einsum( 'ijkl,pq,iq,jpkl', self.hamiltonian.two_body_tensor, pre_matrix_full, kdelta, tpdm, optimize='optimal').real grad_expectation += -1.0 * np.einsum( 'ijkl,pq,jq,ipkl', self.hamiltonian.two_body_tensor, pre_matrix_full, kdelta, tpdm, optimize='optimal').real grad_expectation += -1.0 * np.einsum( 'ijkl,pq,kp,ijlq', self.hamiltonian.two_body_tensor, pre_matrix_full, kdelta, tpdm, optimize='optimal').real grad_expectation += 1.0 * np.einsum( 'ijkl,pq,lp,ijkq', self.hamiltonian.two_body_tensor, pre_matrix_full, kdelta, tpdm, optimize='optimal').real grad[p] = grad_expectation return grad
def rhf_global_gradient(self, params: np.ndarray, alpha_opdm: np.ndarray): """ Compute rhf global gradient Args: params: rhf-parameters for rotation matrix. alpha_opdm: 1-RDM corresponding to results of basis rotation parameterized by `params'. Returns: gradient vector the same size as the input `params' """ opdm = np.zeros((2 * self.num_orbitals, 2 * self.num_orbitals), dtype=np.complex128) opdm[::2, ::2] = alpha_opdm opdm[1::2, 1::2] = alpha_opdm tpdm = 2 * linalg.wedge(opdm, opdm, (1, 1), (1, 1)) # now go through and generate all the necessary Z, Y, Y_kl matrices kappa_matrix = rhf_params_to_matrix(params, len(self.occ) + len(self.virt), self.occ, self.virt) kappa_matrix_full = np.kron(kappa_matrix, np.eye(2)) w_full, v_full = np.linalg.eigh( -1j * kappa_matrix_full) # so that kappa = i U lambda U^ eigs_scaled_full = get_matrix_of_eigs(w_full) grad = np.zeros(self.nocc * self.nvirt, dtype=np.complex128) # kdelta = np.eye(2 * self.num_orbitals) # NOW GENERATE ALL TERMS ASSOCIATED WITH THE GRADIENT!!!!!! for p in range(self.nocc * self.nvirt): grad_params = np.zeros_like(params) grad_params[p] = 1 Y = rhf_params_to_matrix(grad_params, len(self.occ) + len(self.virt), self.occ, self.virt) Y_full = np.kron(Y, np.eye(2)) # Now rotate Y int othe basis that diagonalizes Z Y_kl_full = v_full.conj().T.dot(Y_full).dot(v_full) # now rotate Y_{kl} * (exp(i(l_{k} - l_{l})) - 1) / (i(l_{k} - l_{l})) # into the original basis pre_matrix_full = v_full.dot(eigs_scaled_full * Y_kl_full).dot( v_full.conj().T) grad_expectation = -1.0 * np.einsum( 'ab,pa,pb', self.hamiltonian.one_body_tensor, pre_matrix_full, opdm, optimize='optimal').real grad_expectation += 1.0 * np.einsum( 'ab,bq,aq', self.hamiltonian.one_body_tensor, pre_matrix_full, opdm, optimize='optimal').real grad_expectation += 1.0 * np.einsum( 'ijkl,pi,jpkl', self.hamiltonian.two_body_tensor, pre_matrix_full, tpdm, optimize='optimal').real grad_expectation += -1.0 * np.einsum( 'ijkl,pj,ipkl', self.hamiltonian.two_body_tensor, pre_matrix_full, tpdm, optimize='optimal').real grad_expectation += -1.0 * np.einsum( 'ijkl,kq,ijlq', self.hamiltonian.two_body_tensor, pre_matrix_full, tpdm, optimize='optimal').real grad_expectation += 1.0 * np.einsum( 'ijkl,lq,ijkq', self.hamiltonian.two_body_tensor, pre_matrix_full, tpdm, optimize='optimal').real grad[p] = grad_expectation return grad