Fermion (qiskit_addon_sqd.fermion)

Functions for the study of fermionic systems.

class SCIState(amplitudes, ci_strs_a, ci_strs_b, norb, nelec)[source]

Bases: object

The amplitudes and determinants describing a quantum state.

Parameters:
amplitudes: ndarray

An \(M \times N\) array where \(M =\) len(ci_strs_a) and \(N\) = len(ci_strs_b). amplitudes[i][j] is the amplitude of the determinant pair (ci_strs_a[i], ci_strs_b[j]).

ci_strs_a: ndarray

The alpha determinants.

ci_strs_b: ndarray

The beta determinants.

classmethod load(filename)[source]

Load an SCIState object from an .npz file.

nelec: tuple[int, int]

The numbers of alpha and beta electrons.

norb: int

The number of spatial orbitals.

orbital_occupancies()[source]

Average orbital occupancies.

Return type:

tuple[ndarray, ndarray]

rdm(rank=1, spin_summed=False)[source]

Compute reduced density matrix.

Parameters:
Return type:

ndarray

save(filename)[source]

Save the SCIState object to an .npz file.

spin_square()[source]

Return spin squared.

Return type:

float

class SCIResult(energy, sci_state, orbital_occupancies, rdm1=None, rdm2=None, carryover=None)[source]

Bases: object

Result of an SCI calculation.

Parameters:
carryover: tuple[ndarray, ndarray] | None = None

The CI strings to carry over into the next iteration, chosen by the solver.

A pair (strings_a, strings_b) of arrays of spin-alpha and spin-beta CI strings, each ordered by descending marginal weight. This is the same shape as a subspace, because that is what the strings are used to build.

Set this when the solver ranks and selects determinants itself, which an eigensolver that trims its own subspace is well placed to do. When it is None, diagonalize_fermionic_hamiltonian() selects the carryover from sci_state using its carryover_threshold argument, as it always has.

Note that the next subspace is spanned by the Cartesian product of the two arrays, so it is generally larger than the set of configurations that were ranked. Selecting the 150 highest-weight configurations of a subspace typically involves close to 150 distinct spin-alpha strings and 150 distinct spin-beta ones, whose product spans roughly 22,500 configurations. Use the max_dim argument of diagonalize_fermionic_hamiltonian() to bound the result.

energy: float

The SCI energy.

orbital_occupancies: tuple[ndarray, ndarray]

The average orbital occupancies.

rdm1: ndarray | None = None

Spin-summed 1-particle reduced density matrix.

rdm2: ndarray | None = None

Spin-summed 2-particle reduced density matrix.

sci_state: SCIState | None

The SCI state.

This may be None when the solver does not materialize the eigenvector. In that case, the solver must supply carryover, since the configuration recovery loop would otherwise derive it from the amplitudes.

class SubspacePolicy(*args, **kwargs)[source]

Bases: Protocol

The schedule of one configuration recovery iteration.

A policy decides how an iteration spends the diagonalizations it is given: how the sampled configurations become subspaces, whether the results of one round of diagonalizations are refined into a further round, which result the iteration reports, and which CI strings seed the next iteration.

Pass an implementation as the policy argument of diagonalize_fermionic_hamiltonian(). The default, StandardPolicy, diagonalizes the subsampled batches and keeps the best result. TrimPolicy screens the batches and diagonalizes their merged survivors.

A policy chooses which strings a subspace holds. The constraints on the shape of a subspace belong to the caller, so the loop applies them to whatever a policy returns: the configurations requested through include_configurations go into every subspace, spin symmetrization is enforced when it was asked for, and each spin sector is truncated to max_dim. A policy neither receives nor can override them.

Note

A policy produces only data. It is never given an sci_solver and never performs a diagonalization itself: refine() returns the subspaces to diagonalize and the loop calls the solver with them.

This matters under multi-process execution. Unlike an sci_solver, which every process enters together, a policy runs on the control process alone and its return values are broadcast to the others, so those return values must be picklable. A policy must therefore not call qiskit_addon_sqd.processes.broadcast(), barrier(), or any other collective operation: the other processes are not executing it, and the call would hang.

prepare_subspaces(request)[source]

Build the subspaces to diagonalize first.

Parameters:

request (SubspaceRequest) – The bitstrings, carryover strings, and batch sizes to build from.

Returns:

One (strings_a, strings_b) pair per subspace, each spanning the Cartesian product of the two arrays.

Return type:

list[tuple[ndarray, ndarray]]

refine(results, round_index)[source]

Decide whether the results of a round lead to a further round.

Parameters:
  • results (list[SCIResult]) – The results of the round just completed, one per subspace.

  • round_index (int) – The index of that round, counting from zero.

Returns:

The subspaces for a further round, or None to stop and report this round’s results. Returning subspaces on every call raises ValueError once the round limit is reached.

Return type:

list[tuple[ndarray, ndarray]] | None

select_carryover(result, *, symmetrize_spin=False)[source]

Choose the CI strings that seed the next iteration.

Parameters:
Returns:

The spin-alpha and spin-beta strings, in descending order of weight, since that is the order a truncation to max_dim keeps.

Return type:

tuple[ndarray, ndarray]

select_result(results)[source]

Choose the result an iteration reports, from those of its final round.

The chosen result is what convergence is judged on, what becomes the returned result if its energy is the lowest seen, and what select_carryover() receives.

Parameters:

results (list[SCIResult])

Return type:

SCIResult

class SubspaceRequest(bitstrings, probabilities, carryover_strings_a, carryover_strings_b, norb, nelec, samples_per_batch, num_batches, rng, iteration, symmetrize_spin)[source]

Bases: object

The inputs a SubspacePolicy builds an iteration’s subspaces from.

Parameters:
bitstrings: ndarray

The postselected or recovered bitstrings, as a 2D array of bool with one bitstring per row and the alpha part concatenated on the right-hand side, like this: [b_N, ..., b_0, a_N, ..., a_0].

carryover_strings_a: ndarray

The spin-alpha CI strings carried over from the previous iteration, in descending order of weight. Empty on the first iteration.

carryover_strings_b: ndarray

The spin-beta CI strings carried over from the previous iteration. Equal to carryover_strings_a when spin symmetrization was requested.

iteration: int

The index of the current configuration recovery iteration, counting from zero.

nelec: tuple[int, int]

The numbers of alpha and beta electrons.

norb: int

The number of spatial orbitals.

num_batches: int

The number of batches to build.

probabilities: ndarray

A probability for each of the bitstrings.

rng: Generator

The loop’s random number generator.

Draw from this rather than constructing another, and do not reseed it: the seed argument of diagonalize_fermionic_hamiltonian() is what makes a run reproducible, and it controls this generator alone.

samples_per_batch: int

The number of bitstrings each batch should hold.

symmetrize_spin: bool

Whether the two spin sectors share a single list of CI strings.

This is a constraint on the shape of a subspace, which the loop enforces on whatever a policy returns. It appears here because merging the sectors is not something that can be applied afterwards: the merge happens before the strings are ranked, so that the ranking is over both sectors at once, and concatenating two separately ranked lists would order them differently. A policy that ranks strings must therefore know about it. batch_to_ci_strings() takes it for the same reason.

class StandardPolicy(*, carryover_threshold=0.0001)[source]

Bases: object

The default SubspacePolicy: diagonalize the batches, keep the best.

Each iteration subsamples num_batches batches of samples_per_batch bitstrings, diagonalizes each, and reports the lowest energy among them. The strings of that result whose amplitudes exceed carryover_threshold seed the next iteration.

This is the behavior of diagonalize_fermionic_hamiltonian() when no policy is given, and passing an instance explicitly is equivalent to omitting it.

Initialize the policy.

Parameters:

carryover_threshold (float) – Threshold for carrying over bitstrings with large CI weight. The CI strings with amplitude above this value are carried over.

prepare_subspaces(request)[source]

Subsample the batches independently and build a subspace from each.

Parameters:

request (SubspaceRequest)

Return type:

list[tuple[ndarray, ndarray]]

refine(results, round_index)[source]

Stop after the first round, which diagonalized every batch.

The arguments are unused because this schedule never asks for a second round; they are part of the SubspacePolicy interface.

Parameters:
Return type:

list[tuple[ndarray, ndarray]] | None

select_carryover(result, *, symmetrize_spin=False)[source]

Carry over the strings whose amplitudes exceed the threshold.

Parameters:
Return type:

tuple[ndarray, ndarray]

select_result(results)[source]

Report the batch that reached the lowest energy.

Parameters:

results (list[SCIResult])

Return type:

SCIResult

batch_to_ci_strings(batch, norb, carryover_strings_a=None, carryover_strings_b=None, *, symmetrize_spin=False)[source]

Convert a batch of bitstrings into a pair of per-spin CI string arrays.

The strings of each spin sector are returned in descending order of the number of times they were sampled, with any carryover strings ahead of them. That order is what a later truncation to a maximum dimension keeps, so it matters.

This is the step a subspace policy needs in order to turn subsampled bitstrings into a subspace, and it is exposed so that a policy does not have to reimplement it.

Parameters:
  • batch (ndarray) – A 2D array of bool bitstrings, one per row, with the alpha part concatenated on the right-hand side, like this: [b_N, ..., b_0, a_N, ..., a_0].

  • norb (int) – The number of spatial orbitals.

  • carryover_strings_a (ndarray | None) – Spin-alpha CI strings to place ahead of the sampled ones, in the order they should be kept. Defaults to none.

  • carryover_strings_b (ndarray | None) – The same for spin beta. When symmetrize_spin is set, this is ignored, since the two sectors hold the same strings.

  • symmetrize_spin (bool) – Whether to merge the two spin sectors into a single list of strings, used for both. The merge happens before the strings are ranked, so that the ranking is over both sectors at once rather than within each.

Returns:

The spin-alpha and spin-beta CI string arrays. When symmetrize_spin is set, the two are the same array.

Return type:

tuple[ndarray, ndarray]

bitstring_matrix_to_ci_strs(bitstring_matrix, open_shell=False)[source]

Convert bitstrings (rows) in a bitstring_matrix into integer representations of determinants.

This function separates each bitstring in bitstring_matrix in half, translates them into integer representations, and finally appends them to their respective (spin-up or spin-down) lists. Those lists are sorted and output from this function.

Parameters:
  • bitstring_matrix (ndarray) – A 2D array of bool representations of bit values such that each row represents a single bitstring

  • open_shell (bool) – A flag specifying whether unique configurations from the left and right halves of the bitstrings should be kept separate. If False, configurations from the left and right halves of the bitstrings are combined into a single set of unique configurations. That combined set will be returned for both the left and right bitstrings.

Returns:

A length-2 tuple of determinant lists representing the right (spin-up) and left (spin-down) halves of the bitstrings, respectively.

Return type:

tuple[ndarray, ndarray]

diagonalize_fermionic_hamiltonian(one_body_tensor, two_body_tensor, bit_array, samples_per_batch, norb, nelec, *, num_batches=1, energy_tol=1e-08, occupancies_tol=1e-05, max_iterations=100, sci_solver=None, policy=None, symmetrize_spin=False, max_dim=None, include_configurations=None, initial_occupancies=None, carryover_threshold=None, callback=None, seed=None)[source]

Run the sample-based quantum diagonalization (SQD) algorithm.

Parameters:
  • one_body_tensor (ndarray) – The one-body tensor of the Hamiltonian.

  • two_body_tensor (ndarray) – The two-body tensor of the Hamiltonian.

  • bit_array (BitArray | ndarray) – Array of sampled bitstrings, provided as either a Qiskit BitArray or a two-dimensional NumPy boolean array. Each bitstring should have both the alpha part and beta part concatenated together, with the alpha part concatenated on the right-hand side, like this: [b_N, ..., b_0, a_N, ..., a_0].

  • samples_per_batch (int) – The number of bitstrings to include in each subsampled batch of bitstrings.

  • norb (int) – The number of spatial orbitals.

  • nelec (tuple[int, int]) – The numbers of alpha and beta electrons.

  • num_batches (int) – The number of batches to subsample in each configuration recovery iteration. This argument indirectly controls the dimensions of the diagonalization subspaces. A higher value will yield larger subspace dimensions.

  • energy_tol (float) – Numerical tolerance for convergence of the energy. If the change in energy between iterations is smaller than this value, then the configuration recovery loop will exit, if the occupancies have also converged (see the occupancies_tol argument).

  • occupancies_tol (float) – Numerical tolerance for convergence of the average orbital occupancies. If the maximum change in absolute value of the average occupancy of an orbital between iterations is smaller than this value, then the configuration recovery loop will exit, if the energy has also converged (see the energy_tol argument).

  • max_iterations (int) – Limit on the number of configuration recovery iterations.

  • sci_solver (Callable[[list[tuple[ndarray, ndarray]], ndarray, ndarray, int, tuple[int, int]], list[SCIResult]] | None) –

    Selected configuration interaction solver function.

    Inputs:

    • List of pairs (strings_a, strings_b) of arrays of spin-alpha CI strings and spin-beta CI strings whose Cartesian product give the basis of the subspace in which to perform a diagonalization. Every subspace of an iteration is passed in a single call, so that a solver may diagonalize them concurrently rather than one after another.

    • One-body tensor of the Hamiltonian.

    • Two-body tensor of the Hamiltonian.

    • The number of spatial orbitals.

    • A pair (n_alpha, n_beta) indicating the numbers of alpha and beta electrons.

    Output: List of SCIResult, one per subspace passed in.

    A solver that selects its own carryover CI strings can return them in SCIResult.carryover, in which case they are used in place of the ones this function would select using carryover_threshold. Such a solver may leave SCIResult.sci_state as None, so that it never has to return the eigenvector. TrimPolicy does both.

    A solver may also return fewer results than it was given subspaces, which is how a solver that merges its subspaces reports the single diagonalization it performed over them.

    See the note below for the semantics that apply when this function is invoked collectively from multiple processes.

  • policy (SubspacePolicy | None) –

    The schedule of each iteration: how the sampled configurations become subspaces, whether one round of diagonalizations leads to another, which result the iteration reports, and which CI strings seed the next one. See SubspacePolicy.

    Defaults to StandardPolicy, which diagonalizes the subsampled batches and keeps the best result. TrimPolicy screens the batches instead and diagonalizes their merged survivors.

    A policy returns the subspaces and this function calls sci_solver with them. The two are independent, so any policy works with any solver.

  • symmetrize_spin (bool) – Whether to always merge spin-alpha and spin-beta CI strings into a single list, so that the diagonalization subspace is invariant with respect to the exchange of spin alpha with spin beta. This requires the numbers of alpha and beta electrons to be equal, as well as a single max_dim shared by both spin sectors; otherwise, an error is raised. The invariance ensures that the returned state does not mix components of even and odd total spin, but it does not guarantee that the state is an eigenvector of the total spin operator \(S^2\). Note that merging the two lists increases the number of CI strings in each spin sector by up to a factor of two, so the dimension of the diagonalization subspace can grow by up to a factor of four (less when the lists overlap, which is typical). This growth is still subject to the max_dim limit, if one is set.

  • max_dim (int | tuple[int, int] | None) –

    Limit on the dimension of the spin sectors of the SCI subspace. It can be either:

    • A tuple (max_dim_a, max_dim_b) of integers giving separate limits for the spin-alpha and spin-beta sectors. In this case, the dimension of the SCI subspace won’t exceed max_dim_a * max_dim_b.

    • A single integer specifying a limit that will be used for both the spin-alpha and spin-beta sectors. In this case, the dimension of the SCI subspace won’t exceed max_dim**2.

    • None, in which case no limit is set.

    Note that the dimension limit is set on the spin-sector(s), while the full dimension of the SCI subspace is the product of the dimensions of the individual spin sectors.

  • include_configurations (list[int] | tuple[list[int], list[int]] | ndarray | None) – Configurations to always include in the diagonalization subspace. You can specify either a single list of single-spin strings to use for both spin sectors, or a pair (alpha_strings, beta_strings) of lists of single-spin strings, one for each spin.

  • initial_occupancies (tuple[ndarray, ndarray] | None) – Initial guess for the average occupancies of the orbitals.

  • carryover_threshold (float | None) –

    Threshold for carrying over bitstrings with large CI weight from one iteration of configuration recovery to the next. All single-spin CI strings associated with configurations whose coefficient has absolute value greater than this threshold will be included in the diagonalization subspace for the next iteration. A smaller threshold will retain more configurations, leading to a larger subspace and hence a more costly diagonalization. Defaults to 1e-4.

    This is a setting of the default policy, which chooses the carryover by thresholding amplitudes. Another policy chooses it in its own way, so passing both this and policy raises ValueError; pass the threshold to StandardPolicy instead if you are constructing one explicitly.

  • callback (Callable[[list[SCIResult]], None] | None) – A callback function to be called after each configuration recovery iteration. The function will be passed the output of the sci_solver function, which is a list of (energy, sci_state, occupancies) triplets, where each triplet contains the result of a diagonalization.

  • seed (Generator | int | None) – A seed for the pseudorandom number generator.

Returns:

The estimate of the energy and the SCI state with that energy.

Return type:

SCIResult

Note

This function supports collective multi-process (SPMD) execution using MPI, with a single thread controlling each process (MPI_THREAD_FUNNELED or lower). When it is invoked collectively from all processes, the arguments must agree across processes, and the following semantics apply:

  • The sci_solver step is the only collective operation: all processes participate in it, so an sci_solver implementation can distribute work across every process. The remaining steps of the configuration-recovery loop (preparing the CI strings, processing the diagonalization results, and checking convergence) have no distributed implementation and are performed on the control process alone.

  • The callback, if provided, is invoked on the control process only.

  • The return value is the same on every process: the final result is broadcast from the control process to all ranks.

  • Only the results that a collective sci_solver returns on the control process are consumed; what it returns on the other processes is ignored entirely and need not be meaningful. This lets a solver leave each diagonalization’s energy, state and occupancies on whichever process computed them, rather than gathering them onto every rank. A solver that does so must still ensure the control process receives a result for every subspace, gathering across process groups if it distributed them.

Whether the calling program should be launched under MPI depends on the sci_solver in use. A collective sci_solver, as described above, expects the program to run collectively across all processes. Other implementations require the calling program to run as a single process, outside any MPI/SPMD environment. Some of these implementations might manage their own parallelism internally, for example, by launching mpirun themselves. The default sci_solver is not distributed and falls into this latter category.

A collective sci_solver implementation is not required to raise an exception on a single process when it encounters an error; instead, it can follow fail-stop semantics for the execution context as a whole, aborting all processes collectively. (This describes what such an implementation is permitted to do, not the behavior of the default sci_solver.)

Because every subspace of an iteration arrives in one sci_solver call, a collective implementation may divide the processes into groups and diagonalize several subspaces at once, one group per subspace, rather than giving every subspace all of the processes in turn. How to do so is the implementation’s own concern: this package does not divide the processes, and nothing in its interface describes them. An implementation that does divide them is responsible for confining its collective operations to the group that is performing a given diagonalization, and must not use qiskit_addon_sqd.processes.broadcast() or qiskit_addon_sqd.processes.barrier() for that purpose, as those operate over every process rather than over a group.

How many times per iteration the solver is called, and with how many subspaces, is determined by policy. With TrimPolicy, for instance, it is called twice: once with every batch, and once with the single merged subspace. A solver that divides the processes therefore needs only one such division, which it can build on the first call and reuse, rather than rebuilding it every time. Note also that the number of subspaces is the same on every process, since they are broadcast before the solver is called. That matters because dividing the processes is itself a collective operation: every process must take part and agree on how the division is made. A solver may safely base that decision on the number of subspaces it received, but not on anything that could differ between processes.

enlarge_batch_from_transitions(bitstring_matrix, transition_operators)[source]

Apply the set of transition operators to the configurations represented in bitstring_matrix.

Parameters:
  • bitstring_matrix (ndarray) – A 2D array of bool representations of bit values such that each row represents a single bitstring.

  • transition_operators (ndarray) – A 1D or 2D array I, +, -, and n strings representing the action of the identity, creation, annihilation, or number operators. Each row represents a transition operator.

Returns:

Bitstring matrix representing the augmented set of electronic configurations after applying the excitation operators.

Return type:

ndarray

solve_fermion(bitstring_matrix, /, hcore, eri, *, open_shell=False, spin_sq=None, shift=0.1, **kwargs)[source]

Approximate the ground state given molecular integrals and a set of electronic configurations.

Parameters:
  • bitstring_matrix (tuple[ndarray, ndarray] | ndarray) –

    A set of configurations defining the subspace onto which the Hamiltonian will be projected and diagonalized.

    This may be specified in two ways:

    • Bitstring matrix: A 2D numpy.ndarray of bool values, where each row represents a bitstring. The spin-up configurations should occupy column indices (N, N/2], and the spin-down configurations should occupy column indices (N/2, 0], where N is the number of qubits.

    • CI strings: A tuple of two sequences containing integer representations of spin-up and spin-down determinants, respectively. The expected format is ([a_str_0, ..., a_str_N], [b_str_0, ..., b_str_M]).

  • hcore (ndarray) – Core Hamiltonian matrix representing single-electron integrals

  • eri (ndarray) – Electronic repulsion integrals representing two-electron integrals

  • open_shell (bool) – A flag specifying whether configurations from the left and right halves of the bitstrings should be kept separate. If False, CI strings from the left and right halves of the bitstrings are combined into a single set of unique configurations and used for both the alpha and beta subspaces.

  • spin_sq (float | None) – Target value for the total spin squared for the ground state, \(S^2 = s(s + 1)\). If None, no spin will be imposed.

  • shift (float) – Level shift for states which have different spin. \((H + shift * S^2)|ψ> = E|ψ>\)

  • **kwargs – Keyword arguments to pass to pyscf.fci.selected_ci.kernel_fixed_space

Returns:

  • Minimum energy from SCI calculation

  • The SCI ground state

  • Tuple containing orbital occupancies for spin-up and spin-down orbitals. Formatted as: (array([occ_a_0, ..., occ_a_N]), array([occ_b_0, ..., occ_b_N]))

  • Expectation value of spin-squared

Return type:

tuple[float, SCIState, tuple[ndarray, ndarray], float]

solve_sci(ci_strings, one_body_tensor, two_body_tensor, norb, nelec, *, spin_sq=None, **kwargs)[source]

Diagonalize Hamiltonian in subspace defined by CI strings.

Parameters:
  • ci_strings (tuple[ndarray, ndarray]) – Pair (strings_a, strings_b) of arrays of spin-alpha CI strings and spin-beta CI strings whose Cartesian product give the basis of the subspace in which to perform a diagonalization.

  • one_body_tensor (ndarray) – The one-body tensor of the Hamiltonian.

  • two_body_tensor (ndarray) – The two-body tensor of the Hamiltonian.

  • norb (int) – The number of spatial orbitals.

  • nelec (tuple[int, int]) – The numbers of alpha and beta electrons.

  • spin_sq (float | None) – Target value for the total spin squared for the ground state. If None, no spin will be imposed.

  • **kwargs –

    Keyword arguments to pass to pyscf.fci.selected_ci.kernel_fixed_space

Returns:

The diagonalization result.

Return type:

SCIResult

solve_sci_batch(ci_strings, one_body_tensor, two_body_tensor, norb, nelec, *, spin_sq=None, **kwargs)[source]

Diagonalize Hamiltonian in subspaces.

Parameters:
  • ci_strings (list[tuple[ndarray, ndarray]]) – List of pairs (strings_a, strings_b) of arrays of spin-alpha CI strings and spin-beta CI strings whose Cartesian product give the basis of the subspace in which to perform a diagonalization.

  • one_body_tensor (ndarray) – The one-body tensor of the Hamiltonian.

  • two_body_tensor (ndarray) – The two-body tensor of the Hamiltonian.

  • norb (int) – The number of spatial orbitals.

  • nelec (tuple[int, int]) – The numbers of alpha and beta electrons.

  • spin_sq (float | None) – Target value for the total spin squared for the ground state. If None, no spin will be imposed.

  • **kwargs –

    Keyword arguments to pass to pyscf.fci.selected_ci.kernel_fixed_space

Returns:

The results of the diagonalizations in the subspaces given by ci_strings.

Return type:

list[SCIResult]

optimize_orbitals(bitstring_matrix, /, hcore, eri, k_flat, *, open_shell=False, spin_sq=0.0, num_iters=10, num_steps_grad=10000, learning_rate=0.01, **kwargs)[source]

Optimize orbitals to produce a minimal ground state.

The process involves iterating over 3 steps:

For num_iters iterations:
  • Rotate the integrals with respect to the parameters, k_flat

  • Diagonalize and approximate the groundstate energy and wavefunction amplitudes

  • Optimize k_flat using gradient descent and the wavefunction amplitudes found in Step 2

Refer to Sec. II A 4 for more detailed discussion on this orbital optimization technique.

Deprecated since version 0.13.0: The function qiskit_addon_sqd.fermion.optimize_orbitals() is deprecated as of qiskit-addon-sqd 0.13.0. It will be removed no earlier than v0.15.0. Orbital optimization is now supported by the ffsim package. See https://quantum.cloud.ibm.com/docs/addons/qiskit-addon-sqd/guides/optimize-orbitals for a guide on how to optimize the Hamiltonian basis with ffsim.

Parameters:
  • bitstring_matrix (tuple[ndarray, ndarray] | ndarray) –

    A set of configurations defining the subspace onto which the Hamiltonian will be projected and diagonalized.

    This may be specified in two ways:

    • Bitstring matrix: A 2D numpy.ndarray of bool values, where each row represents a bitstring. The spin-up configurations should occupy column indices (N, N/2], and the spin-down configurations should occupy column indices (N/2, 0], where N is the number of qubits.

    • CI strings: A tuple of two sequences containing integer representations of spin-up and spin-down determinants, respectively. The expected format is ([a_str_0, ..., a_str_N], [b_str_0, ..., b_str_M]).

  • hcore (ndarray) – Core Hamiltonian matrix representing single-electron integrals

  • eri (ndarray) – Electronic repulsion integrals representing two-electron integrals

  • k_flat (ndarray) – 1D array defining the orbital transform, K. The array should specify the upper triangle of the anti-symmetric transform operator in row-major order, excluding the diagonal.

  • open_shell (bool) – A flag specifying whether configurations from the left and right halves of the bitstrings should be kept separate. If False, CI strings from the left and right halves of the bitstrings are combined into a single set of unique configurations and used for both the alpha and beta subspaces.

  • spin_sq (float) – Target value for the total spin squared for the ground state

  • num_iters (int) – The number of iterations of orbital optimization to perform

  • num_steps_grad (int) – The number of steps of gradient descent to perform during each optimization iteration

  • learning_rate (float) – The learning rate to use during gradient descent

  • **kwargs –

    Keyword arguments to pass to pyscf.fci.selected_ci.kernel_fixed_space

Returns:

  • The groundstate energy found during the last optimization iteration

  • An optimized 1D array defining the orbital transform

  • Tuple containing orbital occupancies for spin-up and spin-down orbitals. Formatted as: (array([occ_a_0, ..., occ_a_N]), array([occ_b_0, ..., occ_b_N]))

Return type:

tuple[float, ndarray, tuple[ndarray, ndarray]]

rotate_integrals(hcore, eri, k_flat)[source]

Perform a similarity transform on the integrals.

The transformation is described as:

\[\hat{\widetilde{H}} = \hat{U^{\dagger}}(k)\hat{H}\hat{U}(k)\]

For more information on how \(\hat{U}\) and \(\hat{U^{\dagger}}\) are generated from k_flat and applied to the one- and two-body integrals, refer to Sec. II A 4.

Deprecated since version 0.13.0: The function qiskit_addon_sqd.fermion.rotate_integrals() is deprecated as of qiskit-addon-sqd 0.13.0. It will be removed no earlier than v0.15.0. Orbital optimization is now supported by the ffsim package. See https://quantum.cloud.ibm.com/docs/addons/qiskit-addon-sqd/guides/optimize-orbitals for a guide on how to optimize the Hamiltonian basis with ffsim.

Parameters:
  • hcore (ndarray) – Core Hamiltonian matrix representing single-electron integrals

  • eri (ndarray) – Electronic repulsion integrals representing two-electron integrals

  • k_flat (ndarray) – 1D array defining the orbital transform, K. The array should specify the upper triangle of the anti-symmetric transform operator in row-major order, excluding the diagonal.

Returns:

  • The rotated core Hamiltonian matrix

  • The rotated ERI matrix

Return type:

tuple[ndarray, ndarray]