From db58b51ad3fb171de50ae2f5663eab80a5a87b22 Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 09:27:20 -0500 Subject: [PATCH 01/12] Add a solve_fermion function for compatibility with SQD --- qiskit_addon_dice_solver/dice_solver.py | 116 ++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index b680a4d..48aa601 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -24,6 +24,7 @@ import numpy as np from pyscf import tools +from qiskit_addon_sqd.fermion import bitstring_matrix_to_ci_strs # Ensure the runtime linker can find the local boost binaries at runtime DICE_BIN = os.path.join(os.path.abspath(os.path.dirname(__file__)), "bin") @@ -46,6 +47,121 @@ def __init__(self, command, returncode, log_path): super().__init__(message) +def solve_fermion( + bitstring_matrix: np.ndarray, + /, + hcore: np.ndarray, + eri: np.ndarray, + *, + mpirun_options: Sequence[str] | str | None = None, + working_dir: str | Path | None = None, + spin_sq: float = 0.0, + clean_working_dir: bool = True, +) -> tuple[float, np.ndarray, tuple[np.ndarray, np.ndarray]]: + """ + Approximate the ground state given one- and two-body integrals and a bitstring matrix. + + This solver is designed for compatibility with `qiskit-addon-sqd `_ workflows. + + In order to leverage the multi-processing nature of this tool, the user must specify + the CPU resources to use via the `mpirun_options` argument. + + For example, to use 8 CPU slots in parallel in quiet mode: + + .. code-block:: python + + # Run 8 parallel slots in quiet mode + mpirun_opts = "-quiet -n 8" + # OR + mpirun_opts = ["-quiet", "-n", "8"] + + energy, sci_coeffs, avg_occs = solve_fermion(..., mpirun_options=mpirun_opts) + + For more information on the ``mpirun`` command line options, refer to the `man page `_. + + .. note:: + + Only closed-shell systems are supported. The particle number for both + spin-up and spin-down determinants is expected to be equal. + + .. note:: + + Determinant are interpreted by the ``Dice`` command line application as 5-byte unsigned integers; therefore, only systems + of ``40`` or fewer orbitals are supported. + + Args: + bitstring_matrix: A set of configurations defining the subspace onto which the Hamiltonian + will be projected and diagonalized. This is a 2D array of ``bool`` representations of bit + values such that each row represents a single bitstring. The spin-up configurations + should be specified by column indices in range ``(N, N/2]``, and the spin-down + configurations should be specified by column indices in range ``(N/2, 0]``, where ``N`` + is the number of qubits. + hcore: Core Hamiltonian matrix representing single-electron integrals + eri: Electronic repulsion integrals representing two-electron integrals + mpirun_options: Options controlling the CPU resource allocation for the ``Dice`` command line application. + These command-line options will be passed directly to the ``mpirun`` command line application during + invocation of ``Dice``. These may be formatted as a ``Sequence`` of strings or a single string. If a ``Sequence``, + the elements will be combined into a single, space-delimited string and passed to + ``mpirun``. If the input is a single string, it will be passed to ``mpirun`` as-is. If no + ``mpirun_options`` are provided by the user, ``Dice`` will run on a single MPI slot. For more + information on the ``mpirun`` command line options, refer to the `man page `_. + working_dir: An absolute path to a directory in which intermediate files can be written to and read from. If + no working directory is provided, one will be created using Python's ``tempfile`` module. + spin_sq: Target value for the total spin squared for the ground state. If ``None``, no spin will be imposed. + clean_working_dir: A flag indicating whether to remove the intermediate files used by the ``Dice`` + command line application. If ``False``, the intermediate files will be left in a temporary directory in the + ``working_dir``. + + Returns: + Minimum energy from SCI calculation, SCI coefficients, and average orbital occupancy for spin-up and spin-down orbitals + """ + # Convert bitstring matrix to integer determinants for spin-up/down + ci_strs = bitstring_matrix_to_ci_strs(bitstring_matrix) + num_configurations = len(ci_strs[0]) + num_up = bin(ci_strs[0][0])[2:].count("1") + num_dn = bin(ci_strs[1][0])[2:].count("1") + + # Set up the working directory + working_dir = working_dir or tempfile.gettempdir() + intermediate_dir = Path(tempfile.mkdtemp(prefix="dice_cli_files_", dir=working_dir)) + + # Write the integrals out as an FCI dump for Dice command line app + active_space_path = os.path.join(intermediate_dir, "fcidump.txt") + num_orbitals = hcore.shape[0] + tools.fcidump.from_integrals( + active_space_path, hcore, eri, num_orbitals, (num_up + num_dn) + ) + + _write_input_files( + ci_strs, + active_space_path, + num_up, + num_dn, + num_configurations, + intermediate_dir, + spin_sq, + 1, + ) + + # Navigate to working dir and call Dice + _call_dice(intermediate_dir, mpirun_options) + + # Read outputs and convert outputs + e_dice, sci_coefficients, avg_occupancies = _read_dice_outputs( + intermediate_dir, num_orbitals + ) + + # Clean up the working directory of intermediate files, if desired + if clean_working_dir: + shutil.rmtree(intermediate_dir) + + return ( + e_dice, + sci_coefficients, + (avg_occupancies[:num_orbitals], avg_occupancies[num_orbitals:]), + ) + + def solve_dice( addresses: tuple[Sequence[int], Sequence[int]], active_space_path: str | Path, From f43aa89861c6ff9421fe984e6c087dd9cc3bac7f Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 09:32:00 -0500 Subject: [PATCH 02/12] Add SQD as a dependency --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index caa8bc3..5327fd4 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ packages=find_packages(), package_data={"dice_solver": ["bin/Dice", "bin/*.so*"]}, include_package_data=True, - install_requires=["numpy", "pyscf"], + install_requires=["numpy", "pyscf", "qiskit-addon-sqd>=0.6"], extras_require={ "dev": ["tox>=4.0", "pytest>=8.0"], "docs": [ From c7fb5209df8c04908d0e2e1f01c8e793bd37eee4 Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 09:43:52 -0500 Subject: [PATCH 03/12] Update qiskit_addon_dice_solver/dice_solver.py Co-authored-by: Kevin J. Sung --- qiskit_addon_dice_solver/dice_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index 48aa601..ead1f4c 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -106,7 +106,7 @@ def solve_fermion( ``mpirun_options`` are provided by the user, ``Dice`` will run on a single MPI slot. For more information on the ``mpirun`` command line options, refer to the `man page `_. working_dir: An absolute path to a directory in which intermediate files can be written to and read from. If - no working directory is provided, one will be created using Python's ``tempfile`` module. + no working directory is provided, the system temporary files directory will be used. spin_sq: Target value for the total spin squared for the ground state. If ``None``, no spin will be imposed. clean_working_dir: A flag indicating whether to remove the intermediate files used by the ``Dice`` command line application. If ``False``, the intermediate files will be left in a temporary directory in the From 9ef48024fb2ebb80cebb3e361f66ff22c8c643d9 Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 11:03:52 -0500 Subject: [PATCH 04/12] Add solve_fermion to docs --- qiskit_addon_dice_solver/__init__.py | 4 +++- qiskit_addon_dice_solver/dice_solver.py | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/qiskit_addon_dice_solver/__init__.py b/qiskit_addon_dice_solver/__init__.py index 9749afb..151fd41 100644 --- a/qiskit_addon_dice_solver/__init__.py +++ b/qiskit_addon_dice_solver/__init__.py @@ -21,11 +21,13 @@ :toctree: ../stubs/ :nosignatures: + solve_fermion solve_dice """ -from .dice_solver import solve_dice +from .dice_solver import solve_fermion, solve_dice __all__ = [ + "solve_fermion", "solve_dice", ] diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index 48aa601..58e8982 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -55,7 +55,6 @@ def solve_fermion( *, mpirun_options: Sequence[str] | str | None = None, working_dir: str | Path | None = None, - spin_sq: float = 0.0, clean_working_dir: bool = True, ) -> tuple[float, np.ndarray, tuple[np.ndarray, np.ndarray]]: """ @@ -107,14 +106,18 @@ def solve_fermion( information on the ``mpirun`` command line options, refer to the `man page `_. working_dir: An absolute path to a directory in which intermediate files can be written to and read from. If no working directory is provided, one will be created using Python's ``tempfile`` module. - spin_sq: Target value for the total spin squared for the ground state. If ``None``, no spin will be imposed. clean_working_dir: A flag indicating whether to remove the intermediate files used by the ``Dice`` command line application. If ``False``, the intermediate files will be left in a temporary directory in the ``working_dir``. Returns: - Minimum energy from SCI calculation, SCI coefficients, and average orbital occupancy for spin-up and spin-down orbitals + - Minimum energy from SCI calculation + - SCI coefficients + - Average orbital occupancy """ + # Hard-code target S^2 until supported + spin_sq = 0.0 + # Convert bitstring matrix to integer determinants for spin-up/down ci_strs = bitstring_matrix_to_ci_strs(bitstring_matrix) num_configurations = len(ci_strs[0]) From 75ae70db28826110b1eaa6111dd572d162f26b1d Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 11:09:06 -0500 Subject: [PATCH 05/12] docstring --- qiskit_addon_dice_solver/dice_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index 647ca9e..8babb6c 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -58,7 +58,7 @@ def solve_fermion( clean_working_dir: bool = True, ) -> tuple[float, np.ndarray, tuple[np.ndarray, np.ndarray]]: """ - Approximate the ground state given one- and two-body integrals and a bitstring matrix. + Approximate the ground state of a molecular Hamiltonian given a bitstring matrix defining the Hilbert subspace. This solver is designed for compatibility with `qiskit-addon-sqd `_ workflows. From 01441edea33019078a1d1928712624b728d2f83f Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 14:03:24 -0500 Subject: [PATCH 06/12] Update qiskit_addon_dice_solver/dice_solver.py Co-authored-by: Kevin J. Sung --- qiskit_addon_dice_solver/dice_solver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index 8babb6c..a0f3b4b 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -104,8 +104,8 @@ def solve_fermion( ``mpirun``. If the input is a single string, it will be passed to ``mpirun`` as-is. If no ``mpirun_options`` are provided by the user, ``Dice`` will run on a single MPI slot. For more information on the ``mpirun`` command line options, refer to the `man page `_. - working_dir: An absolute path to a directory in which intermediate files can be written to and read from. If - no working directory is provided, the system temporary files directory will be used. + working_dir: An absolute path to a directory for storing temporary files. If + not provided, the system temporary files directory will be used. clean_working_dir: A flag indicating whether to remove the intermediate files used by the ``Dice`` command line application. If ``False``, the intermediate files will be left in a temporary directory in the ``working_dir``. From 74529a66709d7ab10c46aa14959d009c47ad5398 Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 14:04:23 -0500 Subject: [PATCH 07/12] Update qiskit_addon_dice_solver/dice_solver.py Co-authored-by: Kevin J. Sung --- qiskit_addon_dice_solver/dice_solver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index a0f3b4b..b2b228c 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -106,9 +106,9 @@ def solve_fermion( information on the ``mpirun`` command line options, refer to the `man page `_. working_dir: An absolute path to a directory for storing temporary files. If not provided, the system temporary files directory will be used. - clean_working_dir: A flag indicating whether to remove the intermediate files used by the ``Dice`` - command line application. If ``False``, the intermediate files will be left in a temporary directory in the - ``working_dir``. + clean_working_dir: Whether to delete intermediate files generated by the ``Dice`` command line application. + These files will be stored in a directory created inside ``working_dir``. + If ``False``, then this directory will be preserved. Returns: - Minimum energy from SCI calculation From 986b72cae5158fb5c87cb252a4fd3d87dbcf9294 Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 14:10:00 -0500 Subject: [PATCH 08/12] working --> temp --- qiskit_addon_dice_solver/dice_solver.py | 60 ++++++++++++------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index b2b228c..e65ed03 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -54,8 +54,8 @@ def solve_fermion( eri: np.ndarray, *, mpirun_options: Sequence[str] | str | None = None, - working_dir: str | Path | None = None, - clean_working_dir: bool = True, + temp_dir: str | Path | None = None, + clean_temp_dir: bool = True, ) -> tuple[float, np.ndarray, tuple[np.ndarray, np.ndarray]]: """ Approximate the ground state of a molecular Hamiltonian given a bitstring matrix defining the Hilbert subspace. @@ -104,11 +104,11 @@ def solve_fermion( ``mpirun``. If the input is a single string, it will be passed to ``mpirun`` as-is. If no ``mpirun_options`` are provided by the user, ``Dice`` will run on a single MPI slot. For more information on the ``mpirun`` command line options, refer to the `man page `_. - working_dir: An absolute path to a directory for storing temporary files. If - not provided, the system temporary files directory will be used. - clean_working_dir: Whether to delete intermediate files generated by the ``Dice`` command line application. - These files will be stored in a directory created inside ``working_dir``. - If ``False``, then this directory will be preserved. + temp_dir: An absolute path to a directory for storing temporary files. If not provided, the + system temporary files directory will be used. + clean_temp_dir: Whether to delete intermediate files generated by the ``Dice`` command line application. + These files will be stored in a directory created inside ``temp_dir``. If ``False``, then + this directory will be preserved. Returns: - Minimum energy from SCI calculation @@ -124,9 +124,9 @@ def solve_fermion( num_up = bin(ci_strs[0][0])[2:].count("1") num_dn = bin(ci_strs[1][0])[2:].count("1") - # Set up the working directory - working_dir = working_dir or tempfile.gettempdir() - intermediate_dir = Path(tempfile.mkdtemp(prefix="dice_cli_files_", dir=working_dir)) + # Set up the temp directory + temp_dir = temp_dir or tempfile.gettempdir() + intermediate_dir = Path(tempfile.mkdtemp(prefix="dice_cli_files_", dir=temp_dir)) # Write the integrals out as an FCI dump for Dice command line app active_space_path = os.path.join(intermediate_dir, "fcidump.txt") @@ -146,7 +146,7 @@ def solve_fermion( 1, ) - # Navigate to working dir and call Dice + # Navigate to temp dir and call Dice _call_dice(intermediate_dir, mpirun_options) # Read outputs and convert outputs @@ -154,8 +154,8 @@ def solve_fermion( intermediate_dir, num_orbitals ) - # Clean up the working directory of intermediate files, if desired - if clean_working_dir: + # Clean up the temp directory of intermediate files, if desired + if clean_temp_dir: shutil.rmtree(intermediate_dir) return ( @@ -268,13 +268,11 @@ def solve_dice( def _read_dice_outputs( - working_dir: str | Path, num_orbitals: int + temp_dir: str | Path, num_orbitals: int ) -> tuple[float, np.ndarray, np.ndarray]: """Calculate the estimated ground state energy and average orbitals occupancies from Dice outputs.""" # Read in the avg orbital occupancies - spin1_rdm_dice = np.loadtxt( - os.path.join(working_dir, "spin1RDM.0.0.txt"), skiprows=1 - ) + spin1_rdm_dice = np.loadtxt(os.path.join(temp_dir, "spin1RDM.0.0.txt"), skiprows=1) avg_occupancies = np.zeros(2 * num_orbitals) for i in range(spin1_rdm_dice.shape[0]): if spin1_rdm_dice[i, 0] == spin1_rdm_dice[i, 1]: @@ -285,23 +283,23 @@ def _read_dice_outputs( ) # Read in the estimated ground state energy - file_energy = open(os.path.join(working_dir, "shci.e"), "rb") + file_energy = open(os.path.join(temp_dir, "shci.e"), "rb") bytestring_energy = file_energy.read(8) energy_dice = struct.unpack("d", bytestring_energy)[0] # Construct the SCI wavefunction coefficients from Dice output dets.bin - occs, amps = _read_wave_function_magnitudes(os.path.join(working_dir, "dets.bin")) + occs, amps = _read_wave_function_magnitudes(os.path.join(temp_dir, "dets.bin")) addresses = _addresses_from_occupancies(occs) sci_coefficients = _construct_ci_vec_from_addresses_amplitudes(amps, addresses) return energy_dice, sci_coefficients, avg_occupancies -def _call_dice(working_dir: Path, mpirun_options: Sequence[str] | str | None) -> None: - """Navigate to the working dir, invoke Dice, and navigate back.""" +def _call_dice(temp_dir: Path, mpirun_options: Sequence[str] | str | None) -> None: + """Navigate to the temp dir, invoke Dice, and navigate back.""" script_dir = os.path.dirname(os.path.abspath(__file__)) dice_path = os.path.join(script_dir, "bin", "Dice") - dice_log_path = os.path.join(working_dir, "dice_solver_logfile.log") + dice_log_path = os.path.join(temp_dir, "dice_solver_logfile.log") if mpirun_options: if isinstance(mpirun_options, str): mpirun_options = [mpirun_options] @@ -312,7 +310,7 @@ def _call_dice(working_dir: Path, mpirun_options: Sequence[str] | str | None) -> with open(dice_log_path, "w") as logfile: try: subprocess.run( - dice_call, cwd=working_dir, stdout=logfile, stderr=logfile, check=True + dice_call, cwd=temp_dir, stdout=logfile, stderr=logfile, check=True ) except subprocess.CalledProcessError as e: raise DiceExecutionError( @@ -328,13 +326,13 @@ def _write_input_files( num_up: int, num_dn: int, num_configurations: int, - working_dir: str | Path, + temp_dir: str | Path, spin_sq: float, max_iter: int, ) -> None: - """Prepare the Dice inputs in the working directory.""" - ### Move the FCI Dump to working dir ### - shutil.copy(active_space_path, os.path.join(working_dir, "fcidump.txt")) + """Prepare the Dice inputs in the temp directory.""" + ### Move the FCI Dump to temp dir ### + shutil.copy(active_space_path, os.path.join(temp_dir, "fcidump.txt")) ### Write the input.dat ### num_elec = num_up + num_dn @@ -382,19 +380,19 @@ def _write_input_files( nocc, dummy_det, ] - file1 = open(os.path.join(working_dir, "input.dat"), "w") + file1 = open(os.path.join(temp_dir, "input.dat"), "w") file1.writelines(input_list) file1.close() - ### Write the determinants to working dir ### + ### Write the determinants to temp dir ### up_addr, dn_addr = addresses bytes_up = _address_list_to_bytes(up_addr) bytes_dn = _address_list_to_bytes(dn_addr) - file1 = open(os.path.join(working_dir, "AlphaDets.bin"), "wb") # type: ignore + file1 = open(os.path.join(temp_dir, "AlphaDets.bin"), "wb") # type: ignore for bytestring in bytes_up: file1.write(bytestring) # type: ignore file1.close() - file1 = open(os.path.join(working_dir, "BetaDets.bin"), "wb") # type: ignore + file1 = open(os.path.join(temp_dir, "BetaDets.bin"), "wb") # type: ignore for bytestring in bytes_dn: file1.write(bytestring) # type: ignore file1.close() From af5671caf1852e32a0ecf0160ca5acbdff7157ad Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 14:20:43 -0500 Subject: [PATCH 09/12] Update qiskit_addon_dice_solver/dice_solver.py Co-authored-by: Kevin J. Sung --- qiskit_addon_dice_solver/dice_solver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index e65ed03..8514b0f 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -121,8 +121,8 @@ def solve_fermion( # Convert bitstring matrix to integer determinants for spin-up/down ci_strs = bitstring_matrix_to_ci_strs(bitstring_matrix) num_configurations = len(ci_strs[0]) - num_up = bin(ci_strs[0][0])[2:].count("1") - num_dn = bin(ci_strs[1][0])[2:].count("1") + num_up = format(ci_strs[0][0], "b").count("1") + num_dn = format(ci_strs[1][0], "b").count("1") # Set up the temp directory temp_dir = temp_dir or tempfile.gettempdir() From 90e9913301ffb5b3ea454278aaa546e599105eb8 Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 14:21:01 -0500 Subject: [PATCH 10/12] Update qiskit_addon_dice_solver/dice_solver.py Co-authored-by: Kevin J. Sung --- qiskit_addon_dice_solver/dice_solver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index 8514b0f..36a1858 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -129,7 +129,7 @@ def solve_fermion( intermediate_dir = Path(tempfile.mkdtemp(prefix="dice_cli_files_", dir=temp_dir)) # Write the integrals out as an FCI dump for Dice command line app - active_space_path = os.path.join(intermediate_dir, "fcidump.txt") + active_space_path = intermediate_dir / "fcidump.txt" num_orbitals = hcore.shape[0] tools.fcidump.from_integrals( active_space_path, hcore, eri, num_orbitals, (num_up + num_dn) From bd5506317382049c2f668cfea3b1c16d03ca9f5d Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 14:21:33 -0500 Subject: [PATCH 11/12] readability --- qiskit_addon_dice_solver/dice_solver.py | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index e65ed03..1d45296 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -126,37 +126,37 @@ def solve_fermion( # Set up the temp directory temp_dir = temp_dir or tempfile.gettempdir() - intermediate_dir = Path(tempfile.mkdtemp(prefix="dice_cli_files_", dir=temp_dir)) + dice_dir = Path(tempfile.mkdtemp(prefix="dice_cli_files_", dir=temp_dir)) # Write the integrals out as an FCI dump for Dice command line app - active_space_path = os.path.join(intermediate_dir, "fcidump.txt") + active_space_path = os.path.join(dice_dir, "fcidump.txt") num_orbitals = hcore.shape[0] tools.fcidump.from_integrals( active_space_path, hcore, eri, num_orbitals, (num_up + num_dn) ) _write_input_files( - ci_strs, - active_space_path, - num_up, - num_dn, - num_configurations, - intermediate_dir, - spin_sq, - 1, + addresses=ci_strs, + active_space_path=active_space_path, + num_up=num_up, + num_dn=num_dn, + num_configurations=num_configurations, + dice_dir=dice_dir, + spin_sq=spin_sq, + max_iter=1, ) - # Navigate to temp dir and call Dice - _call_dice(intermediate_dir, mpirun_options) + # Navigate to dice dir and call Dice + _call_dice(dice_dir, mpirun_options) # Read outputs and convert outputs e_dice, sci_coefficients, avg_occupancies = _read_dice_outputs( - intermediate_dir, num_orbitals + dice_dir, num_orbitals ) # Clean up the temp directory of intermediate files, if desired if clean_temp_dir: - shutil.rmtree(intermediate_dir) + shutil.rmtree(dice_dir) return ( e_dice, @@ -268,11 +268,11 @@ def solve_dice( def _read_dice_outputs( - temp_dir: str | Path, num_orbitals: int + dice_dir: str | Path, num_orbitals: int ) -> tuple[float, np.ndarray, np.ndarray]: """Calculate the estimated ground state energy and average orbitals occupancies from Dice outputs.""" # Read in the avg orbital occupancies - spin1_rdm_dice = np.loadtxt(os.path.join(temp_dir, "spin1RDM.0.0.txt"), skiprows=1) + spin1_rdm_dice = np.loadtxt(os.path.join(dice_dir, "spin1RDM.0.0.txt"), skiprows=1) avg_occupancies = np.zeros(2 * num_orbitals) for i in range(spin1_rdm_dice.shape[0]): if spin1_rdm_dice[i, 0] == spin1_rdm_dice[i, 1]: @@ -283,23 +283,23 @@ def _read_dice_outputs( ) # Read in the estimated ground state energy - file_energy = open(os.path.join(temp_dir, "shci.e"), "rb") + file_energy = open(os.path.join(dice_dir, "shci.e"), "rb") bytestring_energy = file_energy.read(8) energy_dice = struct.unpack("d", bytestring_energy)[0] # Construct the SCI wavefunction coefficients from Dice output dets.bin - occs, amps = _read_wave_function_magnitudes(os.path.join(temp_dir, "dets.bin")) + occs, amps = _read_wave_function_magnitudes(os.path.join(dice_dir, "dets.bin")) addresses = _addresses_from_occupancies(occs) sci_coefficients = _construct_ci_vec_from_addresses_amplitudes(amps, addresses) return energy_dice, sci_coefficients, avg_occupancies -def _call_dice(temp_dir: Path, mpirun_options: Sequence[str] | str | None) -> None: - """Navigate to the temp dir, invoke Dice, and navigate back.""" +def _call_dice(dice_dir: Path, mpirun_options: Sequence[str] | str | None) -> None: + """Navigate to the dice dir, invoke Dice, and navigate back.""" script_dir = os.path.dirname(os.path.abspath(__file__)) dice_path = os.path.join(script_dir, "bin", "Dice") - dice_log_path = os.path.join(temp_dir, "dice_solver_logfile.log") + dice_log_path = os.path.join(dice_dir, "dice_solver_logfile.log") if mpirun_options: if isinstance(mpirun_options, str): mpirun_options = [mpirun_options] @@ -310,7 +310,7 @@ def _call_dice(temp_dir: Path, mpirun_options: Sequence[str] | str | None) -> No with open(dice_log_path, "w") as logfile: try: subprocess.run( - dice_call, cwd=temp_dir, stdout=logfile, stderr=logfile, check=True + dice_call, cwd=dice_dir, stdout=logfile, stderr=logfile, check=True ) except subprocess.CalledProcessError as e: raise DiceExecutionError( @@ -326,13 +326,13 @@ def _write_input_files( num_up: int, num_dn: int, num_configurations: int, - temp_dir: str | Path, + dice_dir: str | Path, spin_sq: float, max_iter: int, ) -> None: - """Prepare the Dice inputs in the temp directory.""" - ### Move the FCI Dump to temp dir ### - shutil.copy(active_space_path, os.path.join(temp_dir, "fcidump.txt")) + """Prepare the Dice inputs in the specified directory.""" + ### Move the FCI Dump to dice dir ### + shutil.copy(active_space_path, os.path.join(dice_dir, "fcidump.txt")) ### Write the input.dat ### num_elec = num_up + num_dn @@ -380,19 +380,19 @@ def _write_input_files( nocc, dummy_det, ] - file1 = open(os.path.join(temp_dir, "input.dat"), "w") + file1 = open(os.path.join(dice_dir, "input.dat"), "w") file1.writelines(input_list) file1.close() - ### Write the determinants to temp dir ### + ### Write the determinants to dice dir ### up_addr, dn_addr = addresses bytes_up = _address_list_to_bytes(up_addr) bytes_dn = _address_list_to_bytes(dn_addr) - file1 = open(os.path.join(temp_dir, "AlphaDets.bin"), "wb") # type: ignore + file1 = open(os.path.join(dice_dir, "AlphaDets.bin"), "wb") # type: ignore for bytestring in bytes_up: file1.write(bytestring) # type: ignore file1.close() - file1 = open(os.path.join(temp_dir, "BetaDets.bin"), "wb") # type: ignore + file1 = open(os.path.join(dice_dir, "BetaDets.bin"), "wb") # type: ignore for bytestring in bytes_dn: file1.write(bytestring) # type: ignore file1.close() From b5d9907aae5374c688cdf4222dc8a1c8e51ab819 Mon Sep 17 00:00:00 2001 From: Caleb Johnson Date: Wed, 25 Sep 2024 14:57:23 -0500 Subject: [PATCH 12/12] Add a release note --- qiskit_addon_dice_solver/dice_solver.py | 4 ++-- releasenotes/notes/solve-fermion-b55bf481db6a2a51.yaml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/solve-fermion-b55bf481db6a2a51.yaml diff --git a/qiskit_addon_dice_solver/dice_solver.py b/qiskit_addon_dice_solver/dice_solver.py index f0c2e81..64f93a3 100644 --- a/qiskit_addon_dice_solver/dice_solver.py +++ b/qiskit_addon_dice_solver/dice_solver.py @@ -85,7 +85,7 @@ def solve_fermion( .. note:: - Determinant are interpreted by the ``Dice`` command line application as 5-byte unsigned integers; therefore, only systems + Determinants are interpreted by the ``Dice`` command line application as 5-byte unsigned integers; therefore, only systems of ``40`` or fewer orbitals are supported. Args: @@ -149,7 +149,7 @@ def solve_fermion( # Navigate to dice dir and call Dice _call_dice(dice_dir, mpirun_options) - # Read outputs and convert outputs + # Read and convert outputs e_dice, sci_coefficients, avg_occupancies = _read_dice_outputs( dice_dir, num_orbitals ) diff --git a/releasenotes/notes/solve-fermion-b55bf481db6a2a51.yaml b/releasenotes/notes/solve-fermion-b55bf481db6a2a51.yaml new file mode 100644 index 0000000..601af49 --- /dev/null +++ b/releasenotes/notes/solve-fermion-b55bf481db6a2a51.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Introduced a new function, :func:`qiskit_addon_dice_solver.solve_fermion`, which is intended to be compatible with `qiskit-addon-sqd ` workflows.