def evolve_fqe_givens_unrestricted(wfn: Wavefunction, u: np.ndarray) -> Wavefunction: """Evolve a wavefunction by u generated from a 1-body Hamiltonian. Args: wfn: 2^{n} x 1 vector. u: (n x n) unitary matrix. Returns: New evolved wfn object. """ rotations, diagonal = givens_decomposition_square(u.copy()) # Iterate through each layer and time evolve by the appropriate # fermion operators for layer in rotations: for givens in layer: i, j, theta, phi = givens if not np.isclose(phi, 0): op = of.FermionOperator(((j, 1), (j, 0)), coefficient=-phi) wfn = wfn.time_evolve(1.0, op) if not np.isclose(theta, 0): op = of.FermionOperator( ((i, 1), (j, 0)), coefficient=-1j * theta) + of.FermionOperator( ((j, 1), (i, 0)), coefficient=1j * theta) wfn = wfn.time_evolve(1.0, op) # evolve the last diagonal phases for idx, final_phase in enumerate(diagonal): if not np.isclose(final_phase, 1.0): op = of.FermionOperator(((idx, 1), (idx, 0)), -np.angle(final_phase)) wfn = wfn.time_evolve(1.0, op) return wfn
def evolve_wf_diagonal_coulomb(wf: np.ndarray, vij_mat: np.ndarray, time=1) -> np.ndarray: r"""Utility for testing evolution of a full 2^{n} wavefunction via :math:`exp{-i time * \sum_{i,j, sigma, tau}v_{i, j}n_{i\sigma}n_{j\tau}}.` Args: wf: 2^{n} x 1 vector vij_mat: List[(n//2 x n//2)] matrices Returns: New evolved 2^{n} x 1 vector """ norbs = int(np.log2(wf.shape[0]) / 2) diagonal_coulomb = of.FermionOperator() for i, j in product(range(norbs), repeat=2): for sigma, tau in product(range(2), repeat=2): diagonal_coulomb += of.FermionOperator( ( (2 * i + sigma, 1), (2 * i + sigma, 0), (2 * j + tau, 1), (2 * j + tau, 0), ), coefficient=vij_mat[i, j], ) bigU = expm(-1j * time * of.get_sparse_operator(diagonal_coulomb, n_qubits=2 * norbs)) return bigU @ wf
def two_body_sz_adapted(self): """ Doubles generators each with distinct Sz expectation value. G^{isigma, jtau, ktau, lsigma) for sigma, tau in 0, 1 """ for i, j, k, l in product(range(self.norbs), repeat=4): if i < j and k < l: op_aa = ((2 * i, 1), (2 * j, 1), (2 * k, 0), (2 * l, 0)) op_bb = ((2 * i + 1, 1), (2 * j + 1, 1), (2 * k + 1, 0), (2 * l + 1, 0)) fop_aa = of.FermionOperator(op_aa) fop_aa = fop_aa - of.hermitian_conjugated(fop_aa) fop_bb = of.FermionOperator(op_bb) fop_bb = fop_bb - of.hermitian_conjugated(fop_bb) fop_aa = of.normal_ordered(fop_aa) fop_bb = of.normal_ordered(fop_bb) self.op_pool.append(fop_aa) self.op_pool.append(fop_bb) op_ab = ((2 * i, 1), (2 * j + 1, 1), (2 * k + 1, 0), (2 * l, 0)) fop_ab = of.FermionOperator(op_ab) fop_ab = fop_ab - of.hermitian_conjugated(fop_ab) fop_ab = of.normal_ordered(fop_ab) if not np.isclose(fop_ab.induced_norm(), 0): self.op_pool.append(fop_ab)
def ops_pqrs(): #Doubles pairs = [] for i in range(0, n_spinorbitals): for j in range(i+1, n_spinorbitals): pairs.append([i,j]) for pair1 in range(0, len(pairs)): for pair2 in range(pair1, len(pairs)): a, b = pairs[pair2] i, j = pairs[pair1] two_elec = openfermion.FermionOperator(((b,1),(a,1),(j,0),(i,0)))-openfermion.FermionOperator(((i,1),(j,1),(a,0),(b,0))) if args.filter==False or abs(doubles_hamiltonian[j][i][a][b])>1e-8 or abs(doubles_hamiltonian[b][a][i][j])>1e-8: if args.ccfit == True: parameters.append(molecule.ccsd_double_amps[b][a][j][i]) else: parameters.append(0) SQ_CC_ops.append(two_elec) #Singles for i in range(0, n_spinorbitals): for a in range(i, n_spinorbitals): one_elec = openfermion.FermionOperator(((a,1),(i,0)))-openfermion.FermionOperator(((i,1),(a,0))) if args.filter==False or abs(singles_hamiltonian[a][i])>1e-8 or abs(singles_hamiltonian[i][a])>1e-8: if args.ccfit == True: parameters.append(molecule.ccsd_single_amps[a][i]) else: parameters.append(0) SQ_CC_ops.append(one_elec) return(SQ_CC_ops, parameters)
def test_erpa_eom_ham_h2(): 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) rha_fermion = of.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 * of.wedge(opdm, opdm, (1, 1), (1, 1)) rdms = of.InteractionRDM(opdm, tpdm) dim = reduced_ham.one_body_tensor.shape[0] // 2 full_basis = {} # erpa basis. A, B basis in RPA language cnt = 0 for p, q in product(range(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 = of.FermionOperator( ((2 * q + sigma, 1), (2 * p + sigma, 0))) rs_op = of.FermionOperator( ((2 * r + tau, 1), (2 * s + tau, 0))) erpa_op = of.normal_ordered( of.commutator(qp_op, of.commutator(rha_fermion, rs_op))) true = rdms.expectation(of.get_interaction_operator(erpa_op)) assert np.isclose(true, test)
def _single_term_to_FermionOperator(val, lat_ix1, lat_ix2, ind): ''' Export single term of the hamiltonian to openfermion. Parameters ---------- val: number or 2D array Returns ---------- op: openfermion.FermionOperator ''' try: n_spin1 = val.shape[0] n_spin2 = val.shape[1] op = openfermion.FermionOperator() for spin_ix1 in range(n_spin1): for spin_ix2 in range(n_spin2): ix1 = ind.index((lat_ix1, spin_ix1, n_spin1)) ix2 = ind.index((lat_ix2, spin_ix2, n_spin2)) op += openfermion.FermionOperator(f'{ix1}^ {ix2}', val[spin_ix1, spin_ix2]) return op except: try: ix1 = ind.index((lat_ix1, 0, 1)) ix2 = ind.index((lat_ix2, 0, 1)) return openfermion.FermionOperator(f'{ix1}^ {ix2}', val) except: raise ValueError(f''' Cannot construct fermionic operator with indices {lat_ix1}, {lat_ix2}, value {val}''')
def singlet_t2(self): """ Generate singlet rotations T_{ij}^{ab} = T^(v1a, v2b)_{o1a, o2b} + T^(v1b, v2a)_{o1b, o2a} + T^(v1a, v2a)_{o1a, o2a} + T^(v1b, v2b)_{o1b, o2b} where v1,v2 are indices of the virtual obritals and o1, o2 are indices of the occupied orbitals with respect to the Hartree-Fock reference. """ for oo_i in self.occ: for oo_j in self.occ: for vv_a in self.virt: for vv_b in self.virt: term = of.FermionOperator() for sigma, tau in product(range(2), repeat=2): op = ((2 * vv_a + sigma, 1), (2 * vv_b + tau, 1), (2 * oo_j + tau, 0), (2 * oo_i + sigma, 0)) if (2 * vv_a + sigma == 2 * vv_b + tau or 2 * oo_j + tau == 2 * oo_i + sigma): continue fop = of.FermionOperator(op, coefficient=0.5) fop = fop - of.hermitian_conjugated(fop) fop = of.normal_ordered(fop) term += fop self.op_pool.append(term)
def _get_op_Sm(n_site): assert n_site % 2 == 0 op_Sm = openfermion.FermionOperator() for i in range(5): op_Sm += openfermion.FermionOperator(((2 * i + 1, 1), (2 * i, 0))) return op_Sm
def _get_op_Sp(n_site): assert n_site % 2 == 0 op_Sp = openfermion.FermionOperator() for i in range(n_site // 2): op_Sp += openfermion.FermionOperator(((2 * i, 1), (2 * i + 1, 0))) return op_Sp
def make_excitation_generator( self, indices: typing.Iterable[typing.Tuple[int, int]]) -> QubitHamiltonian: """ Notes ---------- Creates the transformed hermitian generator of UCC type unitaries: M(a^\dagger_{a_0} a_{i_0} a^\dagger{a_1}a_{i_1} ... - h.c.) where the qubit map M depends is self.transformation Parameters ---------- indices : typing.Iterable[typing.Tuple[int, int]] : List of tuples [(a_0, i_0), (a_1, i_1), ... ] - recommended format, in spin-orbital notation (alpha odd numbers, beta even numbers) can also be given as one big list: [a_0, i_0, a_1, i_1 ...] Returns ------- type 1j*Transformed qubit excitation operator, depends on self.transformation """ # check indices and convert to list of tuples if necessary if len(indices) == 0: raise TequilaException( "make_excitation_operator: no indices given") elif not isinstance(indices[0], typing.Iterable): if len(indices) % 2 != 0: raise TequilaException( "make_excitation_generator: unexpected input format of indices\n" "use list of tuples as [(a_0, i_0),(a_1, i_1) ...]\n" "or list as [a_0, i_0, a_1, i_1, ... ]\n" "you gave: {}".format(indices)) converted = [(indices[2 * i], indices[2 * i + 1]) for i in range(len(indices) // 2)] else: converted = indices # convert to openfermion input format ofi = [] dag = [] for pair in converted: assert (len(pair) == 2) ofi += [ (int(pair[0]), 1), (int(pair[1]), 0) ] # openfermion does not take other types of integers like numpy.int64 dag += [(int(pair[0]), 0), (int(pair[1]), 1)] op = openfermion.FermionOperator(tuple(ofi), 1.j) # 1j makes it hermitian op += openfermion.FermionOperator(tuple(reversed(dag)), -1.j) qop = QubitHamiltonian(qubit_hamiltonian=self.transformation(op)) # check if the operator is hermitian and cast coefficients to floats # in order to avoid trouble with the simulation backends assert qop.is_hermitian() for k, v in qop.qubit_operator.terms.items(): qop.qubit_operator.terms[k] = to_float(v) qop = qop.simplify() return qop
def _get_op_Sz(n_site): assert n_site % 2 == 0 op_Sz = openfermion.FermionOperator() for i in range(n_site // 2): op_Sz += openfermion.FermionOperator(((2 * i, 1), (2 * i, 0)), +0.5) # alpha op_Sz += openfermion.FermionOperator(((2 * i + 1, 1), (2 * i + 1, 0)), -0.5) # beta return op_Sz
def evolve_fqe_givens_sector(wfn: Wavefunction, u: np.ndarray, sector='alpha') -> Wavefunction: """Evolve a wavefunction by u generated from a 1-body Hamiltonian. Args: wfn: FQE Wavefunction on n-orbitals u: (n x n) unitary matrix. sector: Optional either 'alpha' or 'beta' indicating which sector to rotate Returns: New evolved wfn object. """ if sector == 'alpha': sigma = 0 elif sector == 'beta': sigma = 1 else: raise ValueError("Bad section variable. Either (alpha) or (beta)") if not np.isclose(u.shape[0], wfn.norb()): raise ValueError( "unitary is not specified for the correct number of orbitals") rotations, diagonal = givens_decomposition_square(u.copy()) # Iterate through each layer and time evolve by the appropriate # fermion operators for layer in rotations: for givens in layer: i, j, theta, phi = givens if not np.isclose(phi, 0): op = of.FermionOperator( ((2 * j + sigma, 1), (2 * j + sigma, 0)), coefficient=-phi) wfn = wfn.time_evolve(1.0, op) if not np.isclose(theta, 0): op = of.FermionOperator(((2 * i + sigma, 1), (2 * j + sigma, 0)), coefficient=-1j * theta) + \ of.FermionOperator(((2 * j + sigma, 1), (2 * i + sigma, 0)), coefficient=1j * theta) wfn = wfn.time_evolve(1.0, op) # evolve the last diagonal phases for idx, final_phase in enumerate(diagonal): if not np.isclose(final_phase, 1.0): op = of.FermionOperator( ((2 * idx + sigma, 1), (2 * idx + sigma, 0)), -np.angle(final_phase)) wfn = wfn.time_evolve(1.0, op) return wfn
def one_body_sz_adapted(self): # alpha-alpha rotation # beta-beta rotation for i, j in product(range(self.norbs), repeat=2): if i > j: op_aa = ((2 * i, 1), (2 * j, 0)) op_bb = ((2 * i + 1, 1), (2 * j + 1, 0)) fop_aa = of.FermionOperator(op_aa) fop_aa = fop_aa - of.hermitian_conjugated(fop_aa) fop_bb = of.FermionOperator(op_bb) fop_bb = fop_bb - of.hermitian_conjugated(fop_bb) fop_aa = of.normal_ordered(fop_aa) fop_bb = of.normal_ordered(fop_bb) self.op_pool.append(fop_aa) self.op_pool.append(fop_bb)
def _test(): s1 = sympy.Symbol('spam') s2 = sympy.Symbol('egg') w1 = WrappedExpr(s1) w2 = WrappedExpr(s2) print(isinstance(w1, float)) print(hash(s1)) print() print(w1 + w2) print(type(w1 + w2)) print() print(w1 + 100) print(type(w1 + 100)) print(type((w1 + 100).expr)) print() print(100 + w1) print(type(100 + w1)) print() print(100. + w1) print(type(100. + w1)) print() c = 100 + 2j print(type(c)) print(c + w1) print(type(c + w1)) print(w1 - w2) print(w1 - w1) print('aaa', pow(w1, w2)) print('aaa', pow(w1, w2, 2)) print('aaa', pow(w1, w2, w1)) print(copy.copy(w1)) print(copy.deepcopy(w1)) import openfermion fop = openfermion.FermionOperator('1^ 2', w1) print(fop) print(fop + fop) fop += fop * 2 + openfermion.FermionOperator('', 1) print(fop)
def evolve_fqe_charge_charge_sector(wfn: Wavefunction, vij_mat: np.ndarray, sector='alpha', time=1) -> Wavefunction: r"""Utility for testing evolution of a full 2^{n} wavefunction via :math:`exp{-i time * \sum_{i,j}v_{i, j}n_{i, alpha}n_{j, beta}}.` Args: wfn: fqe_wf with sdim = n vij_mat: List[(n x n] matrices n is the spatial orbital rank time: evolution time. Returns: New evolved 2^{2 * n} x 1 vector """ if sector == 'alpha': sigma = 0 elif sector == 'beta': sigma = 1 else: raise ValueError("Sector must be alpha or beta.") norbs = vij_mat.shape[0] for p, q in product(range(norbs), repeat=2): if np.isclose(vij_mat[p, q], 0): continue fop = of.FermionOperator(((2 * p + sigma, 1), (2 * p + sigma, 0), (2 * q + sigma, 1), (2 * q + sigma, 0)), coefficient=vij_mat[p, q]) wfn = wfn.time_evolve(time, fop) return wfn
def _get_op_S2(n_site): op_Sp = _get_op_Sp(n_site) op_Sm = _get_op_Sm(n_site) op_Sz = _get_op_Sz(n_site) op_S2 = op_Sp * op_Sm + op_Sz * (op_Sz - openfermion.FermionOperator( (), 1)) return op_S2
def fermion_generator(self) -> openfermion.FermionOperator: """The FermionOperator G such that the gate's unitary is exp(-i t G) with exponent t using the Jordan-Wigner transformation.""" half_generator = sum(( w * G for w, G in zip(self.weights, self.fermion_generator_components())), openfermion.FermionOperator()) return half_generator + openfermion.hermitian_conjugated(half_generator)
def test_simulate_trotter_bad_hamiltonian_type_raises_error(): qubits = cirq.LineQubit.range(2) hamiltonian = openfermion.FermionOperator() time = 1.0 with pytest.raises(TypeError): _ = next(simulate_trotter(qubits, hamiltonian, time, algorithm=None)) with pytest.raises(TypeError): _ = next(simulate_trotter(qubits, hamiltonian, time, algorithm=LINEAR_SWAP_NETWORK))
def generalized_two_body_minimal(self): """ Doubles generators each with distinct Sz expectation value. """ for i, j, k, l in product(range(2 * self.norbs), repeat=4): if i < j and k < l: op = ((i, 1), (j, 1), (k, 0), (l, 0)) fop_aa = of.FermionOperator(op) fop_aa = fop_aa - of.hermitian_conjugated(fop_aa) self.op_pool.append(fop_aa)
def test_double_excitation_matches_fermionic_evolution(half_turns): gate = DoubleExcitation ** half_turns op = openfermion.FermionOperator('3^ 2^ 1 0') op += openfermion.hermitian_conjugated(op) matrix_op = openfermion.get_sparse_operator(op) time_evol_op = scipy.linalg.expm(-1j * matrix_op * half_turns * numpy.pi) time_evol_op = time_evol_op.todense() cirq.testing.assert_allclose_up_to_global_phase( gate.matrix(), time_evol_op, atol=1e-7)
def test_double_excitation_matches_fermionic_evolution(exponent): gate = ofc.DoubleExcitation ** exponent op = openfermion.FermionOperator('3^ 2^ 1 0') op += openfermion.hermitian_conjugated(op) matrix_op = openfermion.get_sparse_operator(op) time_evol_op = scipy.linalg.expm(-1j * matrix_op * exponent * numpy.pi) time_evol_op = time_evol_op.todense() cirq.testing.assert_allclose_up_to_global_phase( cirq.unitary(gate), time_evol_op, atol=1e-7)
def test_op_pool(): op = OperatorPool(2, [0], [1]) op.singlet_t2() true_generator = of.FermionOperator(((1, 1), (0, 1), (3, 0), (2, 0))) - \ of.FermionOperator(((3, 1), (2, 1), (1, 0), (0, 0))) assert np.isclose( of.normal_ordered(op.op_pool[0] - true_generator).induced_norm(), 0) op = OperatorPool(2, [0], [1]) op.two_body_sz_adapted() assert len(op.op_pool) == 20 true_generator0 = of.FermionOperator('1^ 0^ 2 1') - \ of.FermionOperator('2^ 1^ 1 0') assert np.isclose( of.normal_ordered(op.op_pool[0] - true_generator0).induced_norm(), 0) true_generator_end = of.FermionOperator('3^ 0^ 3 2') - \ of.FermionOperator('3^ 2^ 3 0') assert np.isclose( of.normal_ordered(op.op_pool[-1] - true_generator_end).induced_norm(), 0) op = OperatorPool(2, [0], [1]) op.one_body_sz_adapted() for gen in op.op_pool: for ladder_idx, _ in gen.terms.items(): # check if one body terms are generated assert len(ladder_idx) == 2
def test_vbc_time_evolve(): molecule = build_h4square_moleculardata() oei, tei = molecule.get_integrals() nele = molecule.n_electrons nalpha = nele // 2 nbeta = nele // 2 sz = 0 norbs = oei.shape[0] nso = 2 * norbs fqe_wf = fqe.Wavefunction([[nele, sz, norbs]]) fqe_wf.set_wfn(strategy='random') fqe_wf.normalize() nfqe_wf = fqe.get_number_conserving_wavefunction(nele, norbs) nfqe_wf.sector((nele, sz)).coeff = fqe_wf.sector((nele, sz)).coeff _, tpdm = nfqe_wf.sector((nele, sz)).get_openfermion_rdms() d3 = nfqe_wf.sector((nele, sz)).get_three_pdm() adapt = VBC(oei, tei, nalpha, nbeta, iter_max=50) acse_residual = two_rdo_commutator_symm(adapt.reduced_ham.two_body_tensor, tpdm, d3) sos_op = adapt.get_takagi_tensor_decomp(acse_residual, None) test_wf = copy.deepcopy(nfqe_wf) test_wf = sos_op.time_evolve(test_wf) true_wf = copy.deepcopy(nfqe_wf) for v, cc in zip(sos_op.basis_rotation, sos_op.charge_charge): vc = v.conj() new_tensor = np.einsum('pi,si,ij,qj,rj->pqrs', v, vc, -1j * cc, v, vc) if np.isclose(np.linalg.norm(new_tensor), 0): continue fop = of.FermionOperator() for p, q, r, s in product(range(nso), repeat=4): op = ((p, 1), (s, 0), (q, 1), (r, 0)) fop += of.FermionOperator(op, coefficient=new_tensor[p, q, r, s]) fqe_op = build_hamiltonian(1j * fop, conserve_number=True) true_wf = true_wf.time_evolve(1, fqe_op) true_wf = evolve_fqe_givens_unrestricted(true_wf, sos_op.one_body_rotation) assert np.isclose(abs(fqe.vdot(true_wf, test_wf))**2, 1)
def get_opdm(wf, num_orbitals, transform=of.jordan_wigner): opdm_hw = np.zeros((num_orbitals, num_orbitals), dtype=np.complex128) creation_ops = [ of.get_sparse_operator(transform(of.FermionOperator(((p, 1)))), n_qubits=num_orbitals) for p in range(num_orbitals) ] # not using display style objects for p, q in product(range(num_orbitals), repeat=2): operator = creation_ops[p] @ creation_ops[q].conj().transpose() opdm_hw[p, q] = wf.conj().T @ operator @ wf return opdm_hw
def get_fermion_op(coeff_tensor) -> of.FermionOperator: r"""Returns an openfermion.FermionOperator from the given coeff_tensor. Given A[i, j, k, l] of A = \sum_{ijkl}A[i, j, k, l]i^ j^ k^ l return the FermionOperator A. Args: coeff_tensor: Coefficients for 4-mode operator Returns: A FermionOperator object """ if len(coeff_tensor.shape) == 4: nso = coeff_tensor.shape[0] fermion_op = of.FermionOperator() for p, q, r, s in product(range(nso), repeat=4): if p == q or r == s: continue op = ((p, 1), (q, 1), (r, 0), (s, 0)) fop = of.FermionOperator(op, coefficient=coeff_tensor[p, q, r, s]) fermion_op += fop return fermion_op elif len(coeff_tensor.shape) == 2: nso = coeff_tensor.shape[0] fermion_op = of.FermionOperator() for p, q in product(range(nso), repeat=2): oper = ((p, 1), (q, 0)) fop = of.FermionOperator(oper, coefficient=coeff_tensor[p, q]) fermion_op += fop return fermion_op else: raise ValueError( "Arg `coeff_tensor` should have dimension 2 or 4 but has dimension" f" {len(coeff_tensor.shape)}.")
def test_nbody_spin_sectors(): op = of.FermionOperator(((3, 1), (4, 1), (2, 0), (0, 0)), coefficient=1.0 + 0.5j) # op += of.hermitian_conjugated(op) ( coefficient, parity, alpha_sub_ops, beta_sub_ops, ) = gather_nbody_spin_sectors(op) assert np.isclose(coefficient.real, 1.0) assert np.isclose(coefficient.imag, 0.5) assert np.isclose(parity, -1) assert tuple(map(tuple, alpha_sub_ops)) == ((4, 1), (2, 0), (0, 0)) assert tuple(map(tuple, beta_sub_ops)) == ((3, 1), )
def evolve_wf_givens(wfn: np.ndarray, u: np.ndarray) -> np.ndarray: """Utility for testing evolution of a full 2^{n} wavefunction. Args: wfn: 2^{n} x 1 vector. u: (n//2 x n//2) unitary matrix. Returns: New evolved 2^{n} x 1 vector. """ rotations, diagonal = givens_decomposition_square(u.copy()) n_qubits = u.shape[0] * 2 # Iterate through each layer and time evolve by the appropriate # fermion operators for layer in rotations: for givens in layer: i, j, theta, phi = givens op = of.FermionOperator(((2 * j, 1), (2 * j, 0)), coefficient=-phi) op += of.FermionOperator(((2 * j + 1, 1), (2 * j + 1, 0)), coefficient=-phi) wfn = (expm(-1j * of.get_sparse_operator(op, n_qubits=n_qubits)) @ wfn) op = of.FermionOperator( ((2 * i, 1), (2 * j, 0)), coefficient=-1j * theta) + of.FermionOperator( ((2 * j, 1), (2 * i, 0)), coefficient=1j * theta) op += of.FermionOperator( ((2 * i + 1, 1), (2 * j + 1, 0)), coefficient=-1j * theta) + of.FermionOperator( ((2 * j + 1, 1), (2 * i + 1, 0)), coefficient=1j * theta) wfn = (expm(-1j * of.get_sparse_operator(op, n_qubits=n_qubits)) @ wfn) # evolve the last diagonal phases for idx, final_phase in enumerate(diagonal): if not np.isclose(final_phase, 1.0): op = of.FermionOperator(((2 * idx, 1), (2 * idx, 0)), -np.angle(final_phase)) op += of.FermionOperator(((2 * idx + 1, 1), (2 * idx + 1, 0)), -np.angle(final_phase)) wfn = (expm(-1j * of.get_sparse_operator(op, n_qubits=n_qubits)) @ wfn) return wfn
def _tensor_construct(self, rank, conjugates, updown): """ General procedure for evaluating the expected value of second quantized ops :param Int rank: number of second quantized operators to product out :param List conjugates: Indicator of the conjugate type of the second quantized operator :param List updown: SymmOrbitalDensity matrices are index by spatial orbital Indices. This value is self.dim/2 for Fermionic systems. When projecting out expected values to form the marginals, we need to know if the spin-part of the spatial basis funciton. updown is a list corresponding with the conjugates telling us if we are projecting an up spin or down spin. 0 is for up. 1 is for down. Example: to get the 1-RDM alpha-block we would pass the following: rank = 2, conjugates = [-1, 1], updown=[0, 0] :returns: a tensor of (rank) specified by input :rytpe: np.ndarray """ # self.dim/2 because rank is now spatial basis function rank tensor = np.zeros(tuple([int(self.dim / 2)] * rank), dtype=complex) of_con_upper = [ 1 if np.isclose(x, -1) else 0 for x in conjugates[:rank // 2] ] of_con_lower = [ 1 if np.isclose(x, -1) else 0 for x in conjugates[rank // 2:] ] updown_upper = updown[:rank // 2] updown_lower = updown[rank // 2:] for indices in product(range(self.dim // 2), repeat=rank): fop_u = [(2 * x + y, of_con_upper[idx]) for idx, ( x, y) in enumerate(zip(indices[:rank // 2], updown_upper))] fop_l = [(2 * x + y, of_con_lower[idx]) for idx, ( x, y) in enumerate(zip(indices[rank // 2:], updown_lower))] print(fop_u + fop_l[::-1]) op = of.get_sparse_operator(of.FermionOperator( tuple(fop_u + fop_l[::-1])), n_qubits=self.dim) tensor[indices] = (op @ self.rho).diagonal().sum() return tensor
def map_state(self, state: list, *args, **kwargs) -> list: """ Expects a state in spin-orbital ordering Returns the corresponding qubit state in the class encoding :param state: basis-state as occupation number vector in spin orbitals sorted as: [0_up, 0_down, 1_up, 1_down, ... N_up, N_down] with N being the number of spatial orbitals :return: basis-state as qubit state in the corresponding mapping """ """Does a really lazy workaround ... but it works :return: Hartree-Fock Reference as binary-number Parameters ---------- reference_orbitals: list: give list of doubly occupied orbitals default is None which leads to automatic list of the first n_electron/2 orbitals Returns ------- """ # default is a lazy workaround, but it workds n_qubits = 2 * self.n_orbitals spin_orbitals = sorted([i for i, x in enumerate(state) if int(x) == 1]) string = "1.0 [" for i in spin_orbitals: string += str(i) + "^ " string += "]" fop = openfermion.FermionOperator(string, 1.0) op = self(fop) from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits) wfn = wfn.apply_qubitoperator(operator=op) assert (len(wfn.keys()) == 1) key = list(wfn.keys())[0].array return key
def reference_state(self, reference_orbitals: list = None, n_qubits: int = None) -> BitString: """Does a really lazy workaround ... but it works :return: Hartree-Fock Reference as binary-number Parameters ---------- reference_orbitals: list: give list of doubly occupied orbitals default is None which leads to automatic list of the first n_electron/2 orbitals Returns ------- """ if reference_orbitals is None: reference_orbitals = [i for i in range(self.n_electrons // 2)] spin_orbitals = sorted([2 * i for i in reference_orbitals] + [2 * i + 1 for i in reference_orbitals]) if n_qubits is None: n_qubits = 2 * self.n_orbitals string = "" for i in spin_orbitals: string += str(i) + "^ " fop = openfermion.FermionOperator(string, 1.0) op = QubitHamiltonian(qubit_hamiltonian=self.transformation(fop)) from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits) wfn = wfn.apply_qubitoperator(operator=op) assert (len(wfn.keys()) == 1) keys = [k for k in wfn.keys()] return keys[-1]