Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable user-defined MRSQK reference space #210

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/qforte/abc/qsdabc.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ def common_run(self):
self._Hbar,
print_mats=self._verbose,
sort_ret_vals=True)

if self._save_qk_matrices:
print(f'\n\n Save S, Hbar, evals, evecs to `qk_matrices.npz` file.\n')
np.savez('qk_matrices', overlap=self._S, hbar=self._Hbar, \
evals=self._eigenvalues, evecs=self._eigenvectors )

print(f'\n ==> {type(self).__name__} eigenvalues <==')
print('----------------------------------------')
Expand Down
75 changes: 63 additions & 12 deletions src/qforte/qkd/mrsqk.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class MRSQK(QSD):
_nstates_per_ref : int
The number of states for a generated reference :math:`s+1`.

_reference_generator : {"SRQK"}
_reference_generator : {"SRQK", "USER"}
Specifies an algorithm to choose the reference state.

_s : int
Expand Down Expand Up @@ -126,6 +126,8 @@ def run(self,
mr_dt=0.5,
target_root=0,
reference_generator='SRQK',
refs_user_defined=None,
save_qk_matrices=False,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include this keyword in the docstring.

use_phase_based_selection=False,
use_spin_adapted_refs=True,
s_o=4,
Expand Down Expand Up @@ -153,6 +155,9 @@ def run(self,

self._diagonalize_each_step=diagonalize_each_step

self._sa_ref_lst = []
self._save_qk_matrices = save_qk_matrices

if(self._state_prep_type != 'occupation_list'):
raise ValueError("MRSQK implementation can only handle occupation_list reference.")

Expand All @@ -178,9 +183,15 @@ def run(self,
self.build_refs_from_srqk()

print('\n ==> SRQK reference selection complete.')

elif reference_generator.lower()=='user':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
elif reference_generator.lower()=='user':
elif reference_generator.lower() == 'user':

if refs_user_defined == None:
raise ValueError("User-defined spin-adapted references MISSING; set 'refs_user_defined' option.")
else:
self.get_refs_from_user(refs_user_defined)

else:
raise ValueError("Incorrect value passed for reference_generator, can be 'SRQK'.")
raise ValueError("Incorrect value passed for reference_generator, can be 'SRQK', 'user'.")

self.common_run()

Expand All @@ -192,7 +203,8 @@ def set_circuit_variables(self):
if(self._reference_generator=='SRQK'):
self._n_pauli_trm_measures = self._nstates * self._Nl + self._srqk._n_pauli_trm_measures
else:
raise ValueError('Can only count number of paulit term measurements when using SRQK.')
self._n_pauli_trm_measures = self._nstates * self._Nl

# off-diagonal of Hbar (<X> and <Y> of Hadamard test)
self._n_pauli_trm_measures += self._nstates*(self._nstates-1) * self._Nl
# off-diagonal of S (<X> and <Y> of Hadamard test)
Expand All @@ -213,8 +225,10 @@ def print_options_banner(self):
print('\n\n ==> MRSQK options <==')
print('-----------------------------------------------------------')
# General algorithm options.
print('Trial reference state: ', ref_string(self._ref, self._nqb))
print('Trial state preparation method: ', self._state_prep_type)
print('Reference space generator ', self._reference_generator.upper())
if(self._reference_generator.upper()=='SRQK'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(self._reference_generator.upper()=='SRQK'):
if self._reference_generator.upper()=='SRQK':

print('Trial reference state: ', ref_string(self._ref, self._nqb))
print('Trial state preparation method: ', self._state_prep_type)
print('Trotter order (rho): ', self._trotter_order)
print('Trotter number (m): ', self._trotter_number)
print('Use fast version of algorithm: ', str(self._fast))
Expand All @@ -231,10 +245,11 @@ def print_options_banner(self):
print('Target root: ', str(self._target_root))
print('Use det. selection with sign: ', str(self._use_phase_based_selection))
print('Use spin adapted references: ', str(self._use_spin_adapted_refs))
print('Save final MRSQK matrics: ', str(self._save_qk_matrices))

print('\n\n ==> Initial QK options (for ref. selection) <==')
print('-----------------------------------------------------------')
if(self._reference_generator=='SRQK'):
print('\n\n ==> Initial QK options (for ref. selection) <==')
print('-----------------------------------------------------------')
print('Inital Trotter order (rho_o): ', self._trotter_order_o)
print('Inital Trotter number (m_o): ', self._trotter_number_o)
print('Number of initial time evolutions (s_o): ', self._s_o)
Expand Down Expand Up @@ -325,7 +340,8 @@ def build_qk_mats_fast(self):
self._n_pauli_trm_measures += k * (k-1) * self._Nl
self._n_pauli_trm_measures += k * (k-1)

print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}')
if len(evals) > self._target_root:
print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}')

return s_mat, h_mat

Expand Down Expand Up @@ -424,15 +440,16 @@ def build_sa_qk_mats(self):

k = p+1
self._n_classical_params = k
if(k==1):
if(k==1) and (self._reference_generator != 'user'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if(k==1) and (self._reference_generator != 'user'):
if k==1 and self._reference_generator != 'user':

self._n_cnot = self._srqk._n_cnot
else:
self._n_cnot = 2 * Um.get_num_cnots()
self._n_pauli_trm_measures = k * self._Nl + self._srqk._n_pauli_trm_measures
self._n_pauli_trm_measures = k * self._Nl + self._srqk._n_pauli_trm_measures if self._reference_generator != 'user' else k * self._Nl
self._n_pauli_trm_measures += k * (k-1) * self._Nl
self._n_pauli_trm_measures += k * (k-1)

print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}')
if len(evals) > self._target_root:
print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}')

return s_mat, h_mat

Expand Down Expand Up @@ -641,7 +658,6 @@ def build_sa_refs(self):
print('\n\n ==> Final MRSQK reference space summary <==')
print('-----------------------------------------------------------')

self._sa_ref_lst = []
for i in range(self._d):
print('\nRef ', i+1)
print('---------------------------')
Expand All @@ -659,3 +675,38 @@ def build_sa_refs(self):
print(' ', round(temp[0], 4), ' ', basis.str(self._nqb))

self._sa_ref_lst.append(basis_vec)


def set_sa_ref_lst(self, spin_adapted_ref_list):
"""
Parameters
----------
spin_adapted_ref_list : list of lists of tuples
list of spin adapted references defined by user.
Each term in 'spin_adapted_ref_list' represents a configuration state function (CSF);
Each CSF comprises a list of tuples,
while each tuple contains the coefficient and determinant (in the occupation list format).
As an example:
The following list contains two CSF, built from one and two determinants, respectively.
spin_adapted_ref_list = [ [(1.0, [1,1,0,0]), ],
[(0.7071, [0,1,1,0]), (0.7071, [1,0,0,1])],
]
"""
self._sa_ref_lst = spin_adapted_ref_list

def get_refs_from_user(self, refs_user_defined):
"""Get a list of spin adapted references from user input to be used in the MRSQK procedure.
"""
self.set_sa_ref_lst(refs_user_defined)

print('\n\n ==> MRSQK reference space (User-defined) <==')
print('-----------------------------------------------------------')

for i, csf in enumerate(refs_user_defined):
print('\nRef ', i+1)
Comment on lines +705 to +706
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for i, csf in enumerate(refs_user_defined):
print('\nRef ', i+1)
for i, csf in enumerate(refs_user_defined, start=1):
print('\nRef ', i)

print('---------------------------')
for c, det in csf:
qf_det_idx = ref_to_basis_idx(det)
basis = qforte.QubitBasis(qf_det_idx)
print(f'{c:9.4f} {basis.str(self._nqb)}')

15 changes: 10 additions & 5 deletions src/qforte/qkd/nt_srqk.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def run(self,
s=3,
dt=0.5,
target_root=0,
diagonalize_each_step=True
diagonalize_each_step=True,
save_qk_matrices=False,
):

if not self._fast:
Expand All @@ -60,6 +61,8 @@ def run(self,
self._n_cnot = 0
self._n_pauli_trm_measures = 0

self._save_qk_matrices = save_qk_matrices
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that nt_srqk is called before the MRSQK. Is there a risk of MRSQK overwriting the saved QK files from here?


# Print options banner (should done for all algorithms).
self.print_options_banner()

Expand Down Expand Up @@ -181,10 +184,12 @@ def build_qk_mats(self):
self._n_pauli_trm_measures = k * self._Nl
self._n_pauli_trm_measures += k * (k-1) * self._Nl
self._n_pauli_trm_measures += k * (k-1)

print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {0:10} {self._n_pauli_trm_measures:12}')
if (self._print_summary_file):
f.write(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {0:10} {self._n_pauli_trm_measures:12}\n')

if len(evals) > self._target_root:
string = f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {0:10} {self._n_pauli_trm_measures:12}'
print(string)
if (self._print_summary_file):
f.write(f'{string}\n')

if (self._diagonalize_each_step and self._print_summary_file):
f.close()
Expand Down
15 changes: 10 additions & 5 deletions src/qforte/qkd/srqk.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ def run(self,
s=3,
dt=0.5,
target_root=0,
diagonalize_each_step=True
diagonalize_each_step=True,
save_qk_matrices=False,
):

self._s = s
Expand All @@ -58,6 +59,8 @@ def run(self,
self._n_cnot = 0
self._n_pauli_trm_measures = 0

self._save_qk_matrices = save_qk_matrices

# Print options banner (should done for all algorithms).
self.print_options_banner()

Expand Down Expand Up @@ -193,10 +196,12 @@ def build_qk_mats_fast(self):
self._n_pauli_trm_measures = k * self._Nl
self._n_pauli_trm_measures += k * (k-1) * self._Nl
self._n_pauli_trm_measures += k * (k-1)

print(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}')
if (self._print_summary_file):
f.write(f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}\n')

if len(evals) > self._target_root:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this line added?

string = f' {scond:7.2e} {np.real(evals[self._target_root]):+15.9f} {self._n_classical_params:8} {self._n_cnot:10} {self._n_pauli_trm_measures:12}'
print(string)
if (self._print_summary_file):
f.write(f'{string}\n')

if (self._diagonalize_each_step and self._print_summary_file):
f.close()
Expand Down
47 changes: 47 additions & 0 deletions tests/test_user_defined_ref_qk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from pytest import approx
from qforte import system_factory, MRSQK

class TestRefSpaceMRSQK:
def test_H4_Root1_user_defined_ref_space(self):
print('\n')
# FCI Root 1 singlet energy for H4 at 1.5 angstrom in sto-6g basis
e_fci_root1 = -1.839569968502

Rhh = 1.5
mol = system_factory(system_type = 'molecule',
build_type = 'psi4',
basis = 'sto-6g',
mol_geometry = [('H', (0, 0, -3*Rhh/2)),
('H', (0, 0, -Rhh/2)),
('H', (0, 0, Rhh/2)),
('H', (0, 0, 3*Rhh/2))],
symmetry = 'd2h',
multiplicity = 1, # Only singlets will work with QForte
charge = 0,
num_frozen_docc = 0,
num_frozen_uocc = 0,
run_mp2=0,
run_ccsd=0,
run_cisd=0,
run_fci=0)

refs_user_defined = [
[(1.0, [1,1,0,0,1,1,0,0]), ], # 22 00
[(1.0, [0,0,1,1,1,1,0,0]), ], # 02 20
[(1.0, [1,1,0,0,0,0,1,1]), ], # 20 02
]
target_root = 1

alg = MRSQK(mol, trotter_number=100, trotter_order=1, )
alg.run(d = 3, # num of references
s = 3, # num of time steps per ref
mr_dt = 0.5,
target_root = target_root,
reference_generator = 'user',
refs_user_defined = refs_user_defined,
)

E1 = alg.get_ts_energy()

assert E1 == approx(e_fci_root1, abs=1e-5)