Path: blob/main/chemistry-simulations/aux_files/krylov/qchem/hamiltonian.py
583 views
import numpy as np1import itertools23from cudaq import spin456############################################################7def generate_molecular_spin_ham_restricted(h1e, h2e, ecore):89# This function generates the molecular spin Hamiltonian10# H= E_core+sum_{`pq`} h_{`pq`} a_p^dagger a_q +11# 0.5 * h_{`pqrs`} a_p^dagger a_q^dagger a_r a_s12# h1e: one body integrals h_{`pq`}13# h2e: two body integrals h_{`pqrs`}14# `ecore`: constant (nuclear repulsion or core energy in the active space Hamiltonian)1516# Total number of qubits equals the number of spin molecular orbitals17nqubits = 2 * h1e.shape[0]1819# Initialization20one_body_coeff = np.zeros((nqubits, nqubits))21two_body_coeff = np.zeros((nqubits, nqubits, nqubits, nqubits))2223for p in range(nqubits // 2):24for q in range(nqubits // 2):2526# p & q have the same spin <a|a>= <b|b>=127# <a|b>=<b|a>=0 (orthogonal)28one_body_coeff[2 * p, 2 * q] = h1e[p, q]29one_body_coeff[2 * p + 1, 2 * q + 1] = h1e[p, q]3031for r in range(nqubits // 2):32for s in range(nqubits // 2):3334# Same spin (`aaaa`, `bbbbb`) <a|a><a|a>, <b|b><b|b>35two_body_coeff[2 * p, 2 * q, 2 * r,362 * s] = 0.5 * h2e[p, q, r, s]37two_body_coeff[2 * p + 1, 2 * q + 1, 2 * r + 1,382 * s + 1] = 0.5 * h2e[p, q, r, s]3940# Mixed spin(`abab`, `baba`) <a|a><b|b>, <b|b><a|a>41#<a|b>= 0 (orthogonal)42two_body_coeff[2 * p, 2 * q + 1, 2 * r + 1,432 * s] = 0.5 * h2e[p, q, r, s]44two_body_coeff[2 * p + 1, 2 * q, 2 * r,452 * s + 1] = 0.5 * h2e[p, q, r, s]4647return one_body_coeff, two_body_coeff, ecore484950#######################################################################515253def jordan_wigner_one_body(p, q, coef):5455# Diagonal term: 0.5 h_{pp} (I_p - Z_p)56if p == q:57spin_hamiltonian = 0.5 * coef * spin.i(p)58spin_hamiltonian -= 0.5 * coef * spin.z(p)5960# h_`pq`(a_p^dagger a_q + a_q^dagger a_p) = R(h_`pq`) (a_p^dagger a_q + a_q^dagger a_p) +61# `imag` (h_`pq`) (a_p^dagger a_q - a_q^dagger a_p)62# Off-diagonal real part: 0.5 * real(h_{`pq`}) [ X_p (Z_{p+1}^{q-1}) X_q + Y_p (Z_{p+1}^{q-1}) Y_q ]63# Off-diagonal imaginary part: 0.5* `im`(h_`pq`) [y_p (Z_{p+1}^{q-1}) x_q - x_p (Z_{p+1}^{q-1}) y_q]6465else:66if p > q:67p, q = q, p68coef = np.conj(coef)6970# Compute the parity string (Z_{p+1}^{q-1})71z_indices = [i for i in range(p + 1, q)]72parity_string = 1.073for i in z_indices:74parity_string *= spin.z(i)7576spin_hamiltonian = 0.5 * coef.real * spin.x(p) * parity_string * spin.x(77q)78spin_hamiltonian += 0.5 * coef.real * spin.y(79p) * parity_string * spin.y(q)80spin_hamiltonian += 0.5 * coef.imag * spin.y(81p) * parity_string * spin.x(q)82spin_hamiltonian -= 0.5 * coef.imag * spin.x(83p) * parity_string * spin.y(q)8485return spin_hamiltonian868788#############899091def jordan_wigner_two_body(p, q, r, s, coef):9293# Diagonal term: p=r, q=s or p=s,q=r --> (+ h_`pqpq` = + h_`qpqp` = - h_`qppq` = - h_`pqqp`)94#95# exchange operator: h_`pqpq` (a_p^dagger a_q^dagger a_p a_q) + h_`qpqp` (a_q^dagger a_p^dagger a_q a_p)96# p<q: -1/4 (I_p I_q - I_p Z_q - Z_p I_q+Z_p Z_q)97#98# coulomb operator: h_`qppq` (a_q^dagger a_p^dagger a_p a_q) + h_`pqqp` (a_p^dagger a_q^dagger a_q a_p)99# p<q: 1/4 (I_p I_q - I_p Z_q - Z_p I_q + Z_p Z_q)100101if len(set([p, q, r, s])) == 2:102103if p == r:104spin_hamiltonian = -0.25 * coef * spin.i(p) * spin.i(q)105spin_hamiltonian += 0.25 * coef * spin.i(p) * spin.z(q)106spin_hamiltonian += 0.25 * coef * spin.z(p) * spin.i(q)107spin_hamiltonian -= 0.25 * coef * spin.z(p) * spin.z(q)108109elif q == r:110spin_hamiltonian = 0.25 * coef * spin.i(p) * spin.i(q)111spin_hamiltonian -= 0.25 * coef * spin.i(p) * spin.z(q)112spin_hamiltonian -= 0.25 * coef * spin.z(p) * spin.i(q)113spin_hamiltonian += 0.25 * coef * spin.z(p) * spin.z(q)114115# Off-diagonal term with three different sets of non-equal indices116# Number with excitation operator117# + h_`pqqs` = + h_`qpsq` = - h_`qpqs` = - h_`pqsq` and their hermitian conjugate118# Real (h_`pqqs`) (a_p^dagger a_q^dagger a_q a_s + a_s^dagger a_q^dagger a_q a_p) +119# `imag` (h_`pqqs`) (a_p^dagger a_q^dagger a_q a_s - a_s^dagger a_q^dagger a_q a_p)120# p <q <s: (1/4)(Z_{p+1}^{s-1}) [ I_q {real (h_`pqqs`/4) (x_p x_s + y_p y_s) + {`imag` (h_`pqqs`/4) (y_p x_s - x_p y_s)}121# - Z_q {real (h_`pqqs`/4) (x_p x_s + y_p y_s) + `imag`(h_`pqqs`) (y_p x_s -x_p y_s)}]122123if len(set([p, q, r, s])) == 3:124125if q == r:126if p > r:127a, b = s, p128coef = np.conj(coef)129else:130a, b = p, s131c = q132133elif q == s:134if p > r:135a, b = r, p136coef = -1.0 * np.conj(coef)137else:138a, b = p, r139coef *= -1.0140c = q141142elif p == r:143if q > s:144a, b = s, q145coef = -1.0 * np.conj(coef)146else:147a, b = q, s148coef = -1.0 * coef149c = p150151elif p == s:152if q > r:153a, b = r, q154coef = np.conj(coef)155else:156a, b = q, r157c = p158159parity_string = 1.0160z_qubit = [i for i in range(a + 1, b)]161for i in z_qubit:162parity_string *= spin.z(i)163164spin_hamiltonian = 0.25 * coef.real * spin.x(165a) * parity_string * spin.x(b) * spin.i(c)166spin_hamiltonian += 0.25 * coef.real * spin.y(167a) * parity_string * spin.y(b) * spin.i(c)168spin_hamiltonian += 0.25 * coef.imag * spin.y(169a) * parity_string * spin.x(b) * spin.i(c)170spin_hamiltonian -= 0.25 * coef.imag * spin.x(171a) * parity_string * spin.y(b) * spin.i(c)172173spin_hamiltonian -= 0.25 * coef.real * spin.x(174a) * parity_string * spin.x(b) * spin.z(c)175spin_hamiltonian -= 0.25 * coef.real * spin.y(176a) * parity_string * spin.y(b) * spin.z(c)177spin_hamiltonian -= 0.25 * coef.imag * spin.y(178a) * parity_string * spin.x(b) * spin.z(c)179spin_hamiltonian += 0.25 * coef.imag * spin.x(180a) * parity_string * spin.y(b) * spin.z(c)181182# Off-diagonal term with four different sets of non-equal indices183# h_`pqrs` = h_`qpsr` = - h_`qprs` = - h_`pqsr`184# real {h_`pqrs`} (a_p^dagger a_q^dagger a_r a_s + a_s^dagger a_r^dagger a_q a_p) +185# `imag` (h_`pqrs`) (a_p^dagger a_q^dagger a_r a_s - a_s^dagger a_r^dagger a_q a_p)186# p<q<r<s real part: -1/8 (Z_{p+1}^{q-1}) (Z_{r+1}^{s-1}) (x_p x_q x_r x_s - x_p x_q y_r y_s + x_p y_q x_r y_s + x_p y_q y_r x_s187# + y_p x_q x_r y_s + y_p x_q y_r x_s - y_p y_q x_r x_s + y_p y_q y_r y_s)188# p<q<r<s `imag` part: -1/8 (x_p x_q x_r y_s + x_p x_q y_r x_s - x_p y_q x_r x_s + x_p y_q y_r y_s189# - y_p x_q x_r x_s + y_p x_q y_r y_s - y_p y_q x_r y_s - y_p y_q y_r x_s)190# also we need to compute p<r<q<s and p<r<s<q191192elif len(set([p, q, r, s])) == 4:193194if (p > q) ^ (r > s):195coef *= -1.0196197if p < q < r < s:198a, b, c, d = p, q, r, s199200parity_string_a = 1.0201z_qubit_a = [i for i in range(a + 1, b)]202for i in z_qubit_a:203parity_string_a *= spin.z(i)204205parity_string_b = 1.0206z_qubit_b = [i for i in range(c + 1, d)]207for i in z_qubit_b:208parity_string_b *= spin.z(i)209210spin_hamiltonian = -0.125 * coef.real * spin.x(211a) * parity_string_a * spin.x(b) * spin.x(212c) * parity_string_b * spin.x(d)213spin_hamiltonian -= -0.125 * coef.real * spin.x(214a) * parity_string_a * spin.x(b) * spin.y(215c) * parity_string_b * spin.y(d)216spin_hamiltonian += -0.125 * coef.real * spin.x(217a) * parity_string_a * spin.y(b) * spin.x(218c) * parity_string_b * spin.y(d)219spin_hamiltonian += -0.125 * coef.real * spin.x(220a) * parity_string_a * spin.y(b) * spin.y(221c) * parity_string_b * spin.x(d)222spin_hamiltonian += -0.125 * coef.real * spin.y(223a) * parity_string_a * spin.x(b) * spin.x(224c) * parity_string_b * spin.y(d)225spin_hamiltonian += -0.125 * coef.real * spin.y(226a) * parity_string_a * spin.x(b) * spin.y(227c) * parity_string_b * spin.x(d)228spin_hamiltonian -= -0.125 * coef.real * spin.y(229a) * parity_string_a * spin.y(b) * spin.x(230c) * parity_string_b * spin.x(d)231spin_hamiltonian += -0.125 * coef.real * spin.y(232a) * parity_string_a * spin.y(b) * spin.y(233c) * parity_string_b * spin.y(d)234235spin_hamiltonian += 0.125 * coef.imag * spin.x(236a) * parity_string_a * spin.x(b) * spin.x(237c) * parity_string_b * spin.y(d)238spin_hamiltonian += 0.125 * coef.imag * spin.x(239a) * parity_string_a * spin.x(b) * spin.y(240c) * parity_string_b * spin.x(d)241spin_hamiltonian -= 0.125 * coef.imag * spin.x(242a) * parity_string_a * spin.y(b) * spin.x(243c) * parity_string_b * spin.x(d)244spin_hamiltonian += 0.125 * coef.imag * spin.x(245a) * parity_string_a * spin.y(b) * spin.y(246c) * parity_string_b * spin.y(d)247spin_hamiltonian -= 0.125 * coef.imag * spin.y(248a) * parity_string_a * spin.x(b) * spin.x(249c) * parity_string_b * spin.x(d)250spin_hamiltonian += 0.125 * coef.imag * spin.y(251a) * parity_string_a * spin.x(b) * spin.y(252c) * parity_string_b * spin.y(d)253spin_hamiltonian -= 0.125 * coef.imag * spin.y(254a) * parity_string_a * spin.y(b) * spin.x(255c) * parity_string_b * spin.y(d)256spin_hamiltonian -= 0.125 * coef.imag * spin.y(257a) * parity_string_a * spin.y(b) * spin.y(258c) * parity_string_b * spin.x(d)259260elif p < r < q < s:261a, b, c, d = p, r, q, s262263parity_string_a = 1.0264z_qubit_a = [i for i in range(a + 1, b)]265for i in z_qubit_a:266parity_string_a *= spin.z(i)267268parity_string_b = 1.0269z_qubit_b = [i for i in range(c + 1, d)]270for i in z_qubit_b:271parity_string_b *= spin.z(i)272273spin_hamiltonian = -0.125 * coef.real * spin.x(274a) * parity_string_a * spin.x(b) * spin.x(275c) * parity_string_b * spin.x(d)276spin_hamiltonian += -0.125 * coef.real * spin.x(277a) * parity_string_a * spin.x(b) * spin.y(278c) * parity_string_b * spin.y(d)279spin_hamiltonian -= -0.125 * coef.real * spin.x(280a) * parity_string_a * spin.y(b) * spin.x(281c) * parity_string_b * spin.y(d)282spin_hamiltonian += -0.125 * coef.real * spin.x(283a) * parity_string_a * spin.y(b) * spin.y(284c) * parity_string_b * spin.x(d)285spin_hamiltonian += -0.125 * coef.real * spin.y(286a) * parity_string_a * spin.x(b) * spin.x(287c) * parity_string_b * spin.y(d)288spin_hamiltonian -= -0.125 * coef.real * spin.y(289a) * parity_string_a * spin.x(b) * spin.y(290c) * parity_string_b * spin.x(d)291spin_hamiltonian += -0.125 * coef.real * spin.y(292a) * parity_string_a * spin.y(b) * spin.x(293c) * parity_string_b * spin.x(d)294spin_hamiltonian += -0.125 * coef.real * spin.y(295a) * parity_string_a * spin.y(b) * spin.y(296c) * parity_string_b * spin.y(d)297298spin_hamiltonian += 0.125 * coef.imag * spin.x(299a) * parity_string_a * spin.x(b) * spin.x(300c) * parity_string_b * spin.y(d)301spin_hamiltonian -= 0.125 * coef.imag * spin.x(302a) * parity_string_a * spin.x(b) * spin.y(303c) * parity_string_b * spin.x(d)304spin_hamiltonian += 0.125 * coef.imag * spin.x(305a) * parity_string_a * spin.y(b) * spin.x(306c) * parity_string_b * spin.x(d)307spin_hamiltonian += 0.125 * coef.imag * spin.x(308a) * parity_string_a * spin.y(b) * spin.y(309c) * parity_string_b * spin.y(d)310spin_hamiltonian -= 0.125 * coef.imag * spin.y(311a) * parity_string_a * spin.x(b) * spin.x(312c) * parity_string_b * spin.x(d)313spin_hamiltonian -= 0.125 * coef.imag * spin.y(314a) * parity_string_a * spin.x(b) * spin.y(315c) * parity_string_b * spin.y(d)316spin_hamiltonian += 0.125 * coef.imag * spin.y(317a) * parity_string_a * spin.y(b) * spin.x(318c) * parity_string_b * spin.y(d)319spin_hamiltonian -= 0.125 * coef.imag * spin.y(320a) * parity_string_a * spin.y(b) * spin.y(321c) * parity_string_b * spin.x(d)322323elif p < r < s < q:324a, b, c, d = p, r, s, q325326parity_string_a = 1.0327z_qubit_a = [i for i in range(a + 1, b)]328for i in z_qubit_a:329parity_string_a *= spin.z(i)330331parity_string_b = 1.0332z_qubit_b = [i for i in range(c + 1, d)]333for i in z_qubit_b:334parity_string_b *= spin.z(i)335336spin_hamiltonian = -0.125 * coef.real * spin.x(337a) * parity_string_a * spin.x(b) * spin.x(338c) * parity_string_b * spin.x(d)339spin_hamiltonian += -0.125 * coef.real * spin.x(340a) * parity_string_a * spin.x(b) * spin.y(341c) * parity_string_b * spin.y(d)342spin_hamiltonian += -0.125 * coef.real * spin.x(343a) * parity_string_a * spin.y(b) * spin.x(344c) * parity_string_b * spin.y(d)345spin_hamiltonian -= -0.125 * coef.real * spin.x(346a) * parity_string_a * spin.y(b) * spin.y(347c) * parity_string_b * spin.x(d)348spin_hamiltonian -= -0.125 * coef.real * spin.y(349a) * parity_string_a * spin.x(b) * spin.x(350c) * parity_string_b * spin.y(d)351spin_hamiltonian += -0.125 * coef.real * spin.y(352a) * parity_string_a * spin.x(b) * spin.y(353c) * parity_string_b * spin.x(d)354spin_hamiltonian += -0.125 * coef.real * spin.y(355a) * parity_string_a * spin.y(b) * spin.x(356c) * parity_string_b * spin.x(d)357spin_hamiltonian += -0.125 * coef.real * spin.y(358a) * parity_string_a * spin.y(b) * spin.y(359c) * parity_string_b * spin.y(d)360361spin_hamiltonian -= 0.125 * coef.imag * spin.x(362a) * parity_string_a * spin.x(b) * spin.x(363c) * parity_string_b * spin.y(d)364spin_hamiltonian += 0.125 * coef.imag * spin.x(365a) * parity_string_a * spin.x(b) * spin.y(366c) * parity_string_b * spin.x(d)367spin_hamiltonian += 0.125 * coef.imag * spin.x(368a) * parity_string_a * spin.y(b) * spin.x(369c) * parity_string_b * spin.x(d)370spin_hamiltonian += 0.125 * coef.imag * spin.x(371a) * parity_string_a * spin.y(b) * spin.y(372c) * parity_string_b * spin.y(d)373spin_hamiltonian -= 0.125 * coef.imag * spin.y(374a) * parity_string_a * spin.x(b) * spin.x(375c) * parity_string_b * spin.x(d)376spin_hamiltonian -= 0.125 * coef.imag * spin.y(377a) * parity_string_a * spin.x(b) * spin.y(378c) * parity_string_b * spin.y(d)379spin_hamiltonian -= 0.125 * coef.imag * spin.y(380a) * parity_string_a * spin.y(b) * spin.x(381c) * parity_string_b * spin.y(d)382spin_hamiltonian += 0.125 * coef.imag * spin.y(383a) * parity_string_a * spin.y(b) * spin.y(384c) * parity_string_b * spin.x(d)385386return spin_hamiltonian387388389##########################################################390def jordan_wigner_fermion(h_pq, h_pqrs, ecore, tolerance=1e-12):391392# Compute the qubit `hamiltonian` using `jordan` `wigner`393# one-body and two-body integrals could be real or complex.394#395# For one-body integrals: there are two terms (diagonal term (number operator) h_pp and396# off-diagonal term (excitation operator) h_`pq`)397# H_1= sum_`pq` h_{`pq`} a_p^dagger a_q + h.c.398#399# For two-body integrals: There are three different terms400# (diagonal term (coulomb and exchange operator):h_`pqqp`, h_`pqpq`,401# off diagonal terms: (number with excitation operator)h_`pqqr`, h_`pqrp`,402# and (double excitation operator) h_`pqrs`403# H_2 = sum_{`pqrs`} h_`pqrs` a_p^dagger a_q^dagger a_r a_s + h.c.404#405# Jordan Wigner transformation406# a_j^dagger = (Z_{k=1} ^{j-1}) [0.5(X_j - i Y_j)]407# a_j = (Z_{k=1} ^{j-1}) [0.5(X_j + i Y_j)]408#409# Some combination of indices are not allowed because of the `pauli` principles and410# the anti-commutation relations of the creation and annihilation operators.411412spin_hamiltonian = ecore413414nqubit = h_pq.shape[0]415416for p in range(nqubit):417418# Diagonal one-body term (number operator): sum_p h_{pp} a_p^dagger a_p419coef = h_pq[p, p]420if np.abs(coef) > tolerance:421spin_hamiltonian += jordan_wigner_one_body(p, p, coef)422423for p, q in itertools.combinations(range(nqubit), 2):424425# Off-diagonal one-body term (excitation operator): sum_p<q (h_{`pq`} a_p^dagger a_q + h_{`qp`} a_q^dagger a_p)426coef = 0.5 * (h_pq[p, q] + np.conj(h_pq[q, p]))427if np.abs(coef) > tolerance:428spin_hamiltonian += jordan_wigner_one_body(p, q, coef)429430# Diagonal term two-body (coulomb and exchange operators)431# Diagonal term: p=r, q=s or p=s,q=r --> (+ h_`pqpq` = + h_`qpqp` = - h_`qppq` = - h_`pqqp`)432433# exchange operator434coef = h_pqrs[p, q, p, q] + h_pqrs[q, p, q, p]435if np.abs(coef) > tolerance:436spin_hamiltonian += jordan_wigner_two_body(p, q, p, q, coef)437438# coulomb operator439coef = h_pqrs[p, q, q, p] + h_pqrs[q, p, p, q]440if np.abs(coef) > tolerance:441spin_hamiltonian += jordan_wigner_two_body(p, q, q, p, coef)442443for (p, q), (r, s) in itertools.combinations(444itertools.combinations(range(nqubit), 2), 2):445446# h_`pqrs` = - h_`qprs` = -h_`pqsr` = h_`qpsr`447# Four point symmetry if integrals are complex: `pqrs` = `srqp` = `qpsr` = `rspq`448# Eight point symmetry if integrals are real: `pqrs` = `rqps` = `psrq` = `srqp` = `qpsr` = `rspq` = `spqr` = `qrsp`449450451coef=0.5*(h_pqrs[p,q,r,s] + np.conj(h_pqrs[s,r,q,p]) - h_pqrs[q,p,r,s] - np.conj(h_pqrs[s,r,p,q]) \452- h_pqrs[p,q,s,r] - np.conj(h_pqrs[r,s,q,p]) + h_pqrs[q,p,s,r] + np.conj(h_pqrs[r,s,p,q]))453454# Compute number with excitation operator and double excitation operator455if np.abs(coef) > tolerance:456spin_hamiltonian += jordan_wigner_two_body(p, q, r, s, coef)457458# Remove term with zero coefficient.459spin_hamiltonian = spin_hamiltonian.canonicalize().trim(tolerance)460461return spin_hamiltonian462463464############465466467