Release Notes#

0.7.2#

New Features#

  • The AngularMomentum.overlap() property will now warn the user, when this matrix is non-unitary.

  • Added support for using Qiskit Nature with Python 3.12.

Bug Fixes#

  • Fixes the AngularMomentum operator further to also support cases where the number of beta-spin particles exceeds the number of alpha-spin particles.

  • The commutator methods were faultily trying to call normal_order() on their operands, which are not guaranteed to have this method. Now, they no longer call this method and instead it is up to the user to normal-order the result as needed.

0.7.1#

New Features#

Bug Fixes#

  • Fixes the following operators when dealing with non-orthonormal orbitals (for example when using unrestricted spin orbitals): - AnglarMomentum - s_plus_operator() - s_minus_operator() - s_x_operator() - s_y_operator()

    To make the fix take effect, the new additional overlap argument needs to be provided to all of these operators.

    Prior to this fix, none of the operators above were able to resolve any spin contamination and would yield misleadingly “clean” expectation values. See this issue for a more complete discussion.

0.7.0#

Prelude#

Qiskit Nature has been migrated to the qiskit-community Github organization to further emphasize that it is a community-driven project. To reflect this change and because we are onboarding additional codeowners and maintainers, with this version (0.7) we have decided to remove all deprecated code, regardless of the time of its deprecation. This ensures that the new members of the development team do not have a large bulk of legacy code to maintain. This can mean one of two things for you as the end-user:

  1. Nothing, if you already migrated your code and no longer rely on any deprecated features.

  2. Otherwise, you need to migrate your code immediately. If you cannot do that, or want to continue using some of the features that were removed, you should pin your version of Qiskit Nature to 0.6

You can check out the migration guides for details on how to update your code. For more context on the changes around Qiskit Nature and the other application projects as well as the algorithms library in Qiskit, be sure to read this blog post.

New Features#

  • Adds a new lattice class, HexagonalLattice for the generation of hexagonal lattices.

    You construct a hexagonal lattice by specifying the number of rows and columns of hexagons. You can also specify the edge- and on-site-parameters.

    Below is a simple example to illustrate this:

    from qiskit_nature.second_q.hamiltonians.lattices import HexagonalLattice
    
    lattice = HexagonalLattice(
        2,
        3,
        edge_parameter=1.0,
        onsite_parameter=1.5,
    )
    
  • Adds a new lattice class, KagomeLattice for the generation of kagome lattices.

    For example, you can construct a kagome lattice with 4 and 3 unit cells in the x and y direction, respectively, which has weights 1.0 on all edges, weights 1.5 on self-loops and open boundary conditions

    from qiskit_nature.second_q.hamiltonians.lattices import (
        KagomeLattice,
        BoundaryCondition,
    )
    
    kagome = KagomeLattice(
        4,
        3,
        edge_parameter = 1.0,
        onsite_parameter = 1.5,
        boundary_condition = BoundaryCondition.OPEN
    )
    
  • Adds new operator generator functions to allow more fine-grained spin observables. The new functions are:

    All of these functions take the number of spatial orbitals as their only argument and return the constructed FermionicOp.

    This also allows a much simpler implementation of the AngularMomentum which is simply the $S^2$ operator:

    \[S^2 = S^- S^+ + S^z (S^z + 1)\]
  • Introduced a new feature that implements the bosonic operator BosonicOp. Its functionalities are analogous to the FermioniOp, but for commuting bosonic particles. It should be used to represent a bosonic operator, so if one wants to represent the boson number operator it should do for example:

    from qiskit_nature.second_q.operators import BosonicOp
    bosonic_op = BosonicOp({'+_0 -_0': 1}, num_modes=1)
    

    Due to the nature of bosonic particles, this class uses the commutator relations instead of the anti-commutator ones (used by fermionic particles).

  • In order to use the bosonic operator for quantum applications, this feature also introduces the bosonic linear mapper, which allows to map the BosonicOp to the qubit space. This mapper is based on this paper. To use this mapper one can for example:

    from qiskit_nature.second_q.mappers import BosonicLinearMapper
    mapper = BosonicLinearMapper(truncation=1)
    qubit_op = mapper.map(bos_op)
    
  • Adds the SparseLabelOp.permute_indices() method which allows index permutations to be applied to an operator. For example:

    from qiskit_nature.second_q.operators import FermionicOp
    
    op = FermionicOp({"+_0 +_2 -_1 -_3": 1.0}, num_spin_orbitals=4)
    
    permuted_op = op.permute_indices([3, 1, 0, 2])
    print(permuted_op)
    # Fermionic Operator
    # number spin orbitals=4, number terms=1
    #   1.0 * ( +_3 +_0 -_1 -_2 )
    

    This is a very powerful method so caution is advised when using it as other components of the stack may rely on assumptions which are no longer valid after such a permutation (for example the builtin two-qubit reduction of the ParityMapper).

  • The active_orbitals argument of the ActiveSpaceTransformer may now also take a pair of lists of integers, each of which have a length identical to the number of active spatial orbitals. In this case, the first list indicates the alpha- and the second list the beta-spin orbital indices, respectively.

  • Adds a new convenience subclass of the UCC ansatz. Namely, the spin-symmetry-adapted ansatz, PUCCSD, which includes single and double excitations while always pairing the excitations such that both, the number of particles and the total spin, will be preserved.

    You can use it like any of the other UCC-style ansätze, for example:

    from qiskit_nature.second_q.circuit.library import PUCCSD
    from qiskit_nature.second_q.mappers import JordanWignerMapper
    
    ansatz = PUCCSD(
        num_spatial_orbitals=4,
        num_particles=(2, 2),
        qubit_mapper=JordanWignerMapper(),
    )
    
  • Added the new include_imaginary keyword argument to the UCC, UCCSD, and PUCCD classes. When True, an extra ansatz parameter is added to each excitation that controls the imaginary contribution to its evolution. Thus, this setting doubles the total number of ansatz parameters, as compared to the default setting of False.

Upgrade Notes#

  • Support for running with Python 3.7 has been removed. To run Nature you need a minimum Python version of 3.8.

Bug Fixes#

  • The ActiveSpaceTransformer would sometimes set the wrong number of active particles because of a flawed integer rounding. This has now been fixed.

  • Fixes the tutorial for the excited state solvers. In doing so, the EvaluationRule is properly exposed for importing and documenting accordingly.

  • Fixes the logic of the InterleavedQubitMapper to actually perform the interleaving on the second-quantization level rather than the qubit level. This ensures that the actually expected benefits from using an interleaved ordering (for example when mapping a paired double-excitation where all Z terms cancel each other) occur, rather than a naive re-shuffling of the already mapped qubit operator.

  • Fixes the behavior of SpinOp.to_matrix() for operators acting on more than a single spin.

  • Fixes the copy.copy and copy.deepcopy operations for the Tensor class.

  • Fixes a regression in the performance of the map() method

  • Compatibility fix to support optional sparse install under Python 3.11.

  • Fixed the support of use_pauli_sum_op in the UCC and UVCC classes as well as their extensions. This requires version 0.24 or higher of the qiskit-terra package.

0.6.0#

Prelude#

Qiskit Nature 0.6 focuses on refactoring of the mappers module. To that extent, the QubitConverter class has been deprecated in favor of using the various subclasses of QubitMapper directly. As a short example, while you were doing something similar to this until now:

solver = GroundStateEigensolver(
    QubitConverter(ParityMapper(), two_qubit_reduction=True),
    VQE(...),
)
result = solver.solve(problem)

you now simply do the following instead:

solver = GroundStateEigensolver(
    ParityMapper(num_particles=problem.num_particles),
    VQE(...),
)
result = solver.solve(problem)

Check out the migration guide for the QubitConverter for more details. Besides this major refactoring, a few other changes have been done, so be sure to check out the migration guide from 0.5 to 0.6.

New Features#

  • Adds a Tapered Qubit Mapper class. TaperedQubitMapper is to be used as a wrapper of another standard QubitMapper that can apply symmetry reduction techniques to operators at the end of the mapping.

    The following example shows how this class can be constructed from a mapper and a symmetry object.

    driver = PySCFDriver()
    electronic_structure_problem = driver.run()
    h2_op, _ = electronic_structure_problem.second_q_ops()
    mapper = JordanWignerMapper()
    z2_sym = Z2Symmetries(
        symmetries=[Pauli("ZIIZ"), Pauli("ZIZI"), Pauli("ZZII")],
        sq_paulis=[Pauli("IIIX"), Pauli("IIXI"), Pauli("IXII")],
        sq_list=[0, 1, 2],
        tapering_values=[-1, 1, -1],
    )
    tapered_qubit_mapper = TaperedQubitMapper(mapper, z2symmetries=z2_sym)
    qubit_op = tapered_qubit_mapper.map(h2_op)
    
  • Adds the method get_tapered_mapper() to transform a QubitMapper instance into a TaperedQubitMapper based on the properties of the current problem.

    The following example shows how this method can be used to find the symmetries of a problem and create the associate Tapered Qubit Mapper.

    driver = PySCFDriver()
    electronic_structure_problem = driver.run()
    h2_op, _ = electronic_structure_problem.second_q_ops()
    mapper = JordanWignerMapper()
    tapered_qubit_mapper = electronic_structure_problem.get_tapered_mapper(mapper)
    qubit_op = tapered_qubit_mapper.map(h2_op)
    
  • Updates API for the method symmetry_sector_locator() to accept the new Z2Symmetries from quantum_info as well as the legacy Z2Symmetries from the opflow module.

  • Added support for running with Python 3.11. At the the time of the release, Psi4 and Sparse didn’t have a python 3.11 version.

  • Three new methods for creating instances ElectronicDensity have been added:

    1. constructing an empty (or all-zero) density of a given size:

      empty = ElectronicDensity.empty(num_spatial_orbitals=4)
      
    2. constructing an identity density, meaning that the 1-body matrices are initialized with identity matrices

      identity = ElectronicDensity.identity(num_spatial_orbitals=4)
      
    3. constructing from a provided number of particles. This is a shorter variant of the already existing from_orbital_occupation method for the most common use-case.

      num_spatial_orbitals = 4
      num_particles = (2, 2)
      
      two_and_two = ElectronicDensity.from_particle_number(num_spatial_orbitals, num_particles)
      
      # for example now the 1-body matrices will be:
      #   [[1, 0, 0, 0],
      #    [0, 1, 0, 0],
      #    [0, 0, 0, 0],
      #    [0, 0, 0, 0]]
      

    All of the methods above take the optional keyword-argument include_rdm2 which determines whether or not the 2-body matrices are computed based on the constructed 1-body matrices. By default, this is set to True.

  • Added the InterleavedQubitMapper which allows wrapping of another FermionicMapper to produce qubit operators where the alpha- and beta-spin components are arranged in the qubit register in an interleaved rather than blocked order.

    from qiskit_nature.second_q.mappers import JordanWignerMapper, InterleavedQubitMapper
    from qiskit_nature.second_q.operators import FermionicOp
    
    blocked_mapper = JordanWignerMapper()
    interleaved_mapper = InterleavedQubitMapper(blocked_mapper)
    
    ferm_op = FermionicOp({"+_0 -_1": 1}, num_spin_orbitals=4)
    
    blocked_op = blocked_mapper.map(ferm_op)
    # SparsePauliOp(['IIXY', 'IIYY', 'IIXX', 'IIYX'], coeffs=[-0.25j, 0.25, 0.25, 0.25j])
    
    print(interleaved_mapper.map(ferm_op))
    # SparsePauliOp(['IXIY', 'IYIY', 'IXIX', 'IYIX'], coeffs=[-0.25j, 0.25, 0.25, 0.25j])
    

    The example above extends naturally to work with any scenario in which a FermionicMapper may be used like the construction of a HartreeFock initial state or UCC ansatz, for example.

  • Calling the transform_hamiltonian() is now supported, provided that the active space has been prepared properly.

    # assuming we have the total Hamiltonian of our system available:
    total_hamiltonian = ElectronicEnergy(...)
    
    # now we want to reduce it to an active space of 2 electrons in 2 orbitals
    transformer = ActiveSpaceTransformer(2, 2)
    
    # assuming that our total system size is 10 electrons in 10 orbitals:
    transformer.prepare_active_space(10, 10)
    
    # after preparation, this now works as intended
    reduced_hamiltonian = transformer.transform_hamiltonian(total_hamiltonian)
    
  • Calling the transform_hamiltonian() is now supported, provided that the active space has been prepared properly.

    # assuming we have the total Hamiltonian of our system available:
    total_hamiltonian = ElectronicEnergy(...)
    
    # now we want to apply the freeze-core reduction
    transformer = FreezeCoreTransformer()
    
    # since the FreezeCoreTransformer requires molecular system information,
    # we need to create that data structure like so:
    molecule = MoleculeInfo(
        symbols=["Li", "H"],
        coords=[(0.0, 0.0, 0.0), (0.0, 0.0, 1.6)],
    )
    # and since the system size depends on the basis set, we need to provide
    # the total number of spatial orbitals separately:
    total_num_spatial_orbitals = 11  # e.g. the 6-31g basis
    
    # this allows us to prepare the active space correctly like so:
    transformer.prepare_active_space(molecule, total_num_spatial_orbitals)
    
    # after preparation, this now works as intended
    reduced_hamiltonian = transformer.transform_hamiltonian(total_hamiltonian)
    
  • Adds the symmetric_two_body module. This module provides utilities to exploit the inherent symmetries of chemistry-ordered two-body electronic integrals. You may use these to reduce memory consumption of your code, for example like so:

    from pyscf import gto
    from qiskit_nature.second_q.hamiltonians import ElectronicEnergy
    from qiskit_nature.second_q.operators import (
         ElectronicIntegrals,
         PolynomialTensor,
    )
    from qiskit_nature.second_q.operators.symmetric_two_body import S8Integrals
    
    mol = gto.M(atom="H 0 0 0; H 0 0 0.735", basis="631g*")
    
    hamiltonian = ElectronicEnergy(
        ElectronicIntegrals(
            PolynomialTensor(
                {
                    "+-": mol.get_hcore(),
                    "++--": S8Integrals(mol.intor("int2e", aosym=8)),
                },
                validate=False,
            )
        )
    )
    
    print(hamiltonian.second_q_op())
    

    Since these integral containers are integrated into the stack, you can continue to use existing tools such as the BasisTransformer or even the ActiveSpaceTransformer as if you had stored your integrals in standard arrays.

  • Adds the use_symmetry_reduced_integrals setting. When set to True, this will cause objects like for example the FCIDump, QCSchema, or PySCFDriver to attempt and leverage the symmetric_two_body module in order to reduce the memory requirements at runtime.

  • Adds the new Tensor class used internally to consistently deal with n-dimensional tensors throughout the stack. This class also exposes the label_template which allows an end-user to influence the translation procedure implemented in from_polynomial_tensor().

  • Adds the new tensor_unwrapping setting which may be set to False to disable the unwrapping of internally created Tensor objects stored inside of a PolynomialTensor. See also tensor_unwrapping for more details.

  • Adds the new argument num_particles to the ParityMapper which will implement the two qubit reduction without requiring an instance of QubitConverter.

    from qiskit_nature.second_q.drivers import PySCFDriver
    from qiskit_nature.second_q.mappers import ParityMapper
    
    driver = PySCFDriver()
    driver_result = driver.run()
    fermionic_op, _ = driver_result.second_q_ops()
    mapper = ParityMapper(num_particles=(1, 1))
    qubit_op = mapper.map(fermionic_op)
    
  • Extends the VibrationalIntegrals to fall back to using numpy arrays when the optional sparse dependency is not installed.

  • Leverage library opt_einsum, if installed, for sparse-einsum support. This library supports einsum summation directly on sparse objects as described in its documentation.

  • The new keyword argument register_length has been added to the QubitMapper.map() method. This allows the user to set the length of a SparseLabelOp before mapping it (since this length is a lower bound).

  • Improves the QEOM code and implements the calculation of excited state properties and transition amplitudes with QEOM.

    The new functionalities can be used as follows:

    from qiskit.algorithms.optimizers import COBYLA
    from qiskit.primitives import Estimator
    
    from qiskit_nature.units import DistanceUnit
    from qiskit_nature.second_q.algorithms import VQEUCCFactory, GroundStateEigensolver
    from qiskit_nature.second_q.algorithms.excited_states_solvers import QEOM
    from qiskit_nature.second_q.algorithms.excited_states_solvers.qeom import EvaluationRule
    from qiskit_nature.second_q.circuit.library import UCCSD
    from qiskit_nature.second_q.drivers import PySCFDriver
    from qiskit_nature.second_q.mappers import QubitConverter
    from qiskit_nature.second_q.mappers import JordanWignerMapper
    
    optimizer = COBYLA(maxiter=500, disp=False)
    qubit_converter = QubitConverter(
        JordanWignerMapper(), z2symmetry_reduction=None, two_qubit_reduction=False
    )
    
    driver = PySCFDriver(
        atom="H 0 0 0; H 0 0 1.735",
        basis="sto3g",
        charge=0,
        spin=0,
        unit=DistanceUnit.ANGSTROM,
    )
    es_problem = driver.run()
    hamiltonian_op, _ = es_problem.second_q_ops()
    aux_ops = {"hamiltonian": hamiltonian_op}
    
    # Qeom results
    vqe_solver = VQEUCCFactory(Estimator(), UCCSD(), optimizer)
    me_gsc = GroundStateEigensolver(qubit_converter, vqe_solver)
    qeom_solver = QEOM(
        me_gsc, estimator=Estimator(), excitations="sd", aux_eval_rules=EvaluationRule.ALL
    )
    results_qeom = qeom_solver.solve(es_problem, aux_operators=aux_ops)
    
    for n, aux_op_eval in enumerate(results_qeom.aux_operators_evaluated):
        print(f"Properties of eigen state {n}")
        for aux_name, aux_result in aux_op_eval.items():
            print(f" Expectation value of {aux_name} operator: {aux_result}")
    
  • Added public methods symmetry_reduce_clifford() and convert_clifford() and find_taper_op() to allow a step by step tapering of operators.

  • Changed the behavior of the qiskit_nature.second_q.algorithms.GroundStateEigensolver to not raise an error when the user specifies a auxiliary operator which name clashes an internally constructed operator’s name. The new behavior is to apply precedence to the user-defined operators over the builtin ones in case of conflicts. A warning will be logged when this case happens.

  • Added a tolerance parameter tol to control the eigenvalue threshold in the QEOM calculation.

  • Adds the new formatting_precision attribute to all result objects. This attribute sets the number of decimal places to be used when formatting the result object for printing. It defaults to 12.

  • Added qiskit_nature.testing to house testing utilities. Currently it contains some functions for random sampling.

  • Updated the API to allow QubitMapper objects in places where qiskit_nature.second_q.mappers.QubitConverter were previously required. This addition advances toward a future deprecation of QubitConverter. All inputs of type QubitConverter now support QubitMapper objects implementing a transformation from second quantized operators to Pauli operators. Note that the mappers currently do not support qubit reduction techniques.

  • The method map() now supports individual operators as well as lists and dictionaries of operators.

Deprecation Notes#

  • Deprecated the to_matrix() method. The same functionality can be achieved via the qubit-operator after applying the JordanWignerMapper (one only needs to adapt to the different basis state ordering due to the reversed bitstring endianness).

    import numpy as np
    from qiskit_nature.second_q.mappers import JordanWignerMapper
    from qiskit_nature.second_q.operators import FermionicOp
    from qiskit_nature.settings import settings
    
    settings.use_pauli_sum_op = False
    
    op = FermionicOp({"+_0": 1, "-_1": 1})
    mat = op.to_matrix().todense()
    jw = JordanWignerMapper().map(op)
    
    print(np.allclose(mat, jw.to_matrix(), atol=1e-8))  # prints False
    
    for pauli in jw.paulis:
        pauli.x = pauli.x[::-1]
        pauli.z = pauli.z[::-1]
    
    print(np.allclose(mat, jw.to_matrix(), atol=1e-8))  # prints True
    
  • The QubitConverter class is deprecated in favor of using the QubitMapper implementations directly. As a consequence of this, all public properties and function arguments which referred to the QubitConverter by name (e.g. qubit_converter) have been deprecated in favor of properties and function arguments referring to QubitMapper (e.g. qubit_mapper), respectively.

  • The symmetry_sector_locator() method has been deprecated without a direct replacement. This utility is no longer needed in the new workflow which uses QubitMapper instances directly. Qubit tapering can instead now be done using the TaperedQubitMapper which can be constructed easily using the get_tapered_mapper() method.

  • The match_convert argument of the hartree_fock_bitstring_mapped() method has been deprecated without replacement. This utility is no longer needed in the new workflow which uses QubitMapper instances directly.

  • The VQEClient and its matching VQERuntimeResult are now deprecated. Instead, users should migrate their code to use the Qiskit Runtime Primitives. A guide on how to use this can be found here.

  • Deprecates np.ndarray as the return type of the hijkl, hijkl_ba, and hijkl_bb attributes. Instead, these will always be SymmetricTwoBodyIntegrals. Instances of the latter can be used as np.ndarray so in terms of functionality this should not change anything. However, isinstance(integrals, np.ndarray) will not return True for integrals of type SymmetricTwoBodyIntegrals. Additionally, the three FCIDump attributes will no longer accept physicist-ordered two-body tensors in the future.

  • Deprecated the default value (True) of tensor_unwrapping meaning that in the future __getitem__() will return objects of type Tensor.

  • The M, Q, W, V matrix setters and M, Q, W, V matrix standard deviation setters from QEOMResult were pending deprecated and remain computable from the H and S matrices.

  • QubitMapper.allows_two_qubit_reduction has been deprecated. There is no replacement because it is no longer needed in the new design.

  • All arguments in the QubitMapper API (and its subclasses) which were previously called nmodes have been renamed to register_length.

Bug Fixes#

  • The commutator methods commutator(), anti_commutator(), and double_commutator() no longer faultily simplify the returned operator (i.e. the absolute tolerance during simplification is set to zero instead of defaulting to SparseLabelOp.atol).

  • Fixes the behavior of is_zero() when called on a parameterized operator.

  • Fixes a bug when multiplying a SparseLabelOp with numpy numeric types from the left.

  • Fixes the normal_order() method, which in turn corrects the commutation relations of this operator type.

  • Fixes the VQEClient to work properly with the latest code.

  • Added missing Gaussian native libraries from package qiskit_nature.second_q.drivers.gaussiand.gauopen to the wheels file distribution.

  • Fixes a bug in which BogoliubovTransform would sometimes throw an error due to an inability to cast complex numbers to floats.

  • Fix support of BackendV2 in the VQEClient. Previously, backends instantiated with the IBMProvider failed since they return backends of type BackendV2, which were not correctly supported in the VQE client. Backends instantiated with the IBMQ provider continue to work as before.

0.5.0#

Prelude#

Qiskit Nature 0.5 comes with a major redesign of the BaseProblem layer of its stack. Rather than tightly integrating drivers and transformers, problems are now a lot more standalone and are generated by the various drivers (or built out by a user to their custom needs directly). As a short example comparing the previous to the new problem creation:

problem = ElectronicStructureProblem(driver, [transformer])

The new design works as follows:

problem = driver.to_problem(include_dipole=True)

reduced_problem = transformer.transform(problem)

Check out the migration guide for electronic structure calculations for more details. Furthermore, as a general refactoring strategy the code was migrated from locations qiskit_nature.X.second_quantization to qiskit_nature.X.second_q. However, in doing so some classes/modules were re-categorized, so be sure to check out the detailed migration guide for more details.

New Features#

  • The refactoring of the electronic structure stack has enabled the development of third-party plugins allowing classical codes to call Qiskit Nature instead of relying on the development of drivers in the Qiskit Nature package. One example is the new Qiskit Nature PySCF Plugin which can be used like so:

    from pyscf import gto, scf, mcscf
    
    from qiskit.algorithms.optimizers import SLSQP
    from qiskit.primitives import Estimator
    from qiskit_nature.second_q.algorithms import GroundStateEigensolver, VQEUCCFactory
    from qiskit_nature.second_q.circuit.library import UCCSD
    from qiskit_nature.second_q.mappers import ParityMapper, QubitConverter
    
    from qiskit_nature_pyscf import QiskitSolver
    
    mol = gto.M(atom="Li 0 0 0; H 0 0 1.6", basis="sto-3g")
    
    h_f = scf.RHF(mol).run()
    
    norb, nelec = 2, 2
    
    cas = mcscf.CASCI(h_f, norb, nelec)
    
    converter = QubitConverter(ParityMapper(), two_qubit_reduction=True)
    
    vqe = VQEUCCFactory(Estimator(), UCCSD(), SLSQP())
    
    algorithm = GroundStateEigensolver(converter, vqe)
    
    cas.fcisolver = QiskitSolver(algorithm)
    
    cas.run()
    

    Qiskit Nature still provides drivers to enable simple testing and provide a more accessible entry to Qiskit Nature for users who do not come from a classical chemistry computing background. Check out the migration guide for electronic structure calculations to learn how to update your code to use the refactored drivers.

  • The properties concept has been largely redesigned and is a lot more refined now. Instead of being a “catchall” for operator factories, the module has been cleanly separated into various components. Check out the corresponding section of the migration guide for electronic structure calculations for more details.

  • The vibrational structure stack has been refactored in-line with the changes to the electronic structure stack mentioned previously. However, changes to this stack also include corrections to the differentiation of real-space and second-quantized coefficients of the Watson hamiltonian. For more details, check out the migration guide for vibrational structure calculations.

  • The lattices and related LatticeModel classes have undergone some API changes, particularly around the location of the utility methods for uniform lattice generation. For more details check out the migration guide for lattice models.

  • Added the qiskit_nature.second_q.properties.HeisenbergModel which implements the Hamiltonian of the Heisenberg model. This model is used in the study of critical points and phase transitions of magnetic systems. Through the choice of the model constants and the external magnetic field, we can produce many models like: XYZ, XXX, Ising model and others.

    from qiskit_nature.second_q.hamiltonians import HeisenbergModel
    from qiskit_nature.second_q.hamiltonians.lattices import LineLattice, BoundaryCondition
    
    line_lattice = LineLattice(num_nodes=2, boundary_condition=BoundaryCondition.OPEN)
    heisenberg_model = HeisenbergModel(lattice=line_lattice)
    print(heisenberg_model.second_q_ops())
    # Output: X_0 X_1 * (-1+0j) + Y_0 Y_1 * (-1+0j) + Z_0 Z_1 * (-1+0j)
    
    # These tuples allow us to define a Ising model using the HeisenbergModel
    J = (0.0, 0.0, -1.0)
    B = (1.0, 0.0, 0.0)
    
    ising_model_hm = HeisenbergModel(lattice = line_lattice, coupling_constants = J, ext_magnetic_field = B)
    print(ising_model_hm.second_q_ops())
    # Output: Z_0 Z_1 * (1+0j) + X_0 * (1+0j) + X_1 * (1+0j)
    
  • Adds .SparseLabelOp.equiv for checking approximate equality between two SparseLabelOps.

  • Adds a new Property for the electronic structure stack to evaluate the 1- and 2-body reduced density matrices. Assuming that you already have an instance of your qiskit_nature.second_q.problems.ElectronicStructureProblem, you can add the qiskit_nature.second_q.properties.ElectronicDensity to it like so:

    problem: ElectronicStructureProblem = ...
    
    from qiskit_nature.second_q.properties import ElectronicDensity
    
    # initialize the density in an orthonormal basis simply based on the
    # orbital occupation numbers
    alpha_occupation = [1.0, 1.0, 0.0, 0.0]
    beta_occupation = [1.0, 1.0, 0.0, 0.0]
    
    problem.properties.electronic_density = ElectronicDensity.from_orbital_occupation(
      alpha_occupation, beta_occupation
    )
    
  • Adds the PropertiesContainer and its subclasses to simplify the handling of SparseLabelOpsFactory instances inside of problems. This container is a MutableSet and enforces at most a single instance of any Property kind to be stored inside of it This is sufficient for all application purposes of the auxiliary operators (which are generated by these objects).

    from qiskit_nature.second_q.problems import ElectronicPropertiesContainer
    from qiskit_nature.second_q.properties import ParticleNumber
    
    container = ElectronicPropertiesContainer()
    
    container.particle_number = ParticleNumber(10, 10)
    print(ParticleNumber in container)  # True
    
    container.particle_number = None
    print(ParticleNumber in container)  # False
    
    class MyCustomProperty:
        # implements the SparseLabelOpsFactory protocol
        ...
    
    custom = MyCustomProperty()
    container.add(custom)
    print(custom in container)  # True
    
    container.discard(MyCustomProperty)
    print(custom in container)  # False
    
  • Adds the new keyword argument mirror to the SUCCD ansatz, which allows the inclusion of symmetrically mirrored double excitations while preserving the number of circuit parameters.

    from qiskit_nature.second_q.circuit.library.ansatzes import SUCCD
    from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter
    converter = QubitConverter(JordanWignerMapper())
    ansatz = SUCCD(converter, (1, 1), 6, mirror=True)
    
  • Adds support for the QCSchema via which we aim to standardize the I/O between classical drivers and Qiskit Nature.

  • Implements both HartreeFock and VSCF as subclasses of BlueprintCircuit. This allows the respective classes to be instantiated without explicitly setting all of their instance attributes. Missing attributes can be set at a later point to complete the respective circuit definitions.

    from qiskit_nature.second_q.circuit.library import HartreeFock, VSCF
    from qiskit_nature.second_q.mappers import JordanWignerMapper, QubitConverter
    
    # Initialize Hartree-Fock initial_state without specifying
    # the number of particles and qubit converter.
    hf_state = HartreeFock(num_spatial_orbitals=4)
    
    # ...
    
    # complete circuit definition by specifying the rest of the instance attributes
    hf_state.qubit_converter = QubitConverter(JordanWignerMapper())
    hf_state.num_particles = (1,1)
    
    # ...
    
    # Similarly for VSCF
    vscf_state = VSCF()
    
    # ...
    
    # complete circuit definition by specifying the rest of the instance attributes
    vscf_state.num_modals = [2, 2]
    
    # ...
    
  • The new qiskit_nature.second_q.operators.FermionicOp replaces the old qiskit_nature.operators.second_quantization.operators.FermionicOp. This new operator is a subclass of the qiskit_nature.second_q.operators.SparseLabelOp and, as such, only support sparse labels. It is initialized with a dictionary, mapping sparse label keys to coefficients. It supports the usual algebra for operator addition, scalar multiplication, operator composition, operator tensoring, and complex conjugation. It also provides methods for sorting, equality and equivalency checking, operator simplification, normal ordering, and the computation of induced norms as well as hermiticity properties. Finally, it can also be converted to matrices in the occupation number basis.

    from qiskit_nature.second_q.operators import FermionicOp
    
    op1 = FermionicOp({"+_0 -_1": 1.0}, register_length=2)
    
    # scalar multiplication
    op2 = 2.0 * op1
    
    # operator addition
    op1 + op2
    
    # operator composition
    op1 @ op2
    
    # operator tensoring
    op1 ^ op2
    
    # complex conjugation
    op1.adjoint()
    
    # equality and equivalency checks
    op1 == op2
    op1.equiv(op2)
    
    # simplification and normal ordering
    op3 = FermionicOp({"+_0 -_0 +_0 -_0": 1.0}, register_length=2)
    op3.simplify()
    op3.normal_ordered()
    
    # sorting
    op4 = FermionicOp({"+_0": 2, "+_2": 1, "+_1": 1}, register_length=3)
    op4.sort(weight=False)
    op4.sort(weight=True)
    
    # matrix conversion
    op3.to_matrix(sparse=True)
    op3.to_matrix(sparse=False)
    
    # norm and hermiticity computation
    op1.induced_norm()
    op1.is_hermitian()
    
  • Following the MP2 T2 and energy correction calculation fix, the APIs for MP2InitialPoint and HFInitialPoint have been changed slightly. After setting the grouped_property, the total_energy and energy_correction are now accessed via their respective properties, rather than via get_energy and get_energy_correction.

  • Adds atol parameter to SparseLabelOp.is_hermitian().

  • The supported excitation types in the QEOM code have been updated. It now exposes the full set of excitation generation functionalities provided by the internally used UCC and UVCC ansatze. In particular, this means that rather than providing a custom list of excitation tuples, a function can be set by the user which generates such a custom list. The documentation has been updated accordingly to reflect this in all places.

  • Changes usage of library retworkx to the new substitute rustworkx.

Deprecation Notes#

  • The protein folding stack has been migrated to Qiskit Research. Thus, all of its components in Qiskit Nature have been deprecated.

  • The BOPESSampler has been deprecated without direct replacement. For the future direction in which classical codes will be calling Qiskit Nature (instead of the use of drivers) the features provided by the sampler are no longer the responsibility of Qiskit Nature. A warm-starting InitialPoint utility has been suggested. You can subscribe to that feature request to receive updates.

  • Deprecated all code that was migrated to qiskit_nature/second_q according to issue: https://github.com/Qiskit/qiskit-nature/issues/749

    The following table provides a rough overview of the old and new package locations. In certain cases, single classes might have been treated differently. Be sure to check out the referenced issues or the migration guide for more details.

    Old

    New

    qiskit_nature.algorithms

    qiskit_nature.second_q.algorithms

    qiskit_nature.algorithms.pes_samplers

    Removed. For more details, refer to: https://github.com/Qiskit/qiskit-nature/issues/750

    qiskit_nature.circuit

    qiskit_nature.second_q.circuit

    qiskit_nature.converters

    qiskit_nature.second_q.mappers

    qiskit_nature.drivers

    qiskit_nature.second_q.drivers and qiskit_nature.second_q.formats

    qiskit_nature.mappers

    qiskit_nature.second_q.mappers

    qiskit_nature.operators

    qiskit_nature.second_q.operators

    qiskit_nature.problems

    qiskit_nature.second_q.problems

    qiskit_nature.problems.sampling

    Moved to qiskit-research. For more details, refer to: https://github.com/qiskit-research/qiskit-research

    qiskit_nature.properties

    qiskit_nature.second_q.hamiltonians and qiskit_nature.second_q.properties For more details, refer to: https://github.com/Qiskit/qiskit-nature/issues/705

    qiskit_nature.results

    qiskit_nature.second_q.problems

    qiskit_nature.transformers

    qiskit_nature.second_q.transformers

  • The utility methods building out the QEOM hopping operators have been moved away from the BaseProblem interface and have been attached to the QEOM implementation itself.

Bug Fixes#

  • Fix the initial point classes to account for the number of times the evolved operators are repeated in the ansatz.

  • Fixes the compatibility of the fermionic excitation generator options which disables spin preserving while also enabling generalized behavior. Now, combining generalized=True with preserve_spin=False results in all combinations of excitations in the given spin orbital space. De-excitations are not included and filtered accordingly.

  • Fixes a bug where numpy integer objects were causing integer-based isinstance checks to fail. This also avoids such problems by explicitly converting integer values to Python integers when loading properties from HDF5 files.

  • Fixed the MP2 T2 amplitudes and energy correction computation to ensure it matches the result from PySCF.

  • Fixes an issue in the Gaussian Log parsing when the A to H data is provided across multiple instead of on a single line in the file.

  • Fixes the behavior of FermionicOp.simplify when called on a zero-operator.

  • Fixes the qiskit_nature.runtime.VQEClient to correctly detect the type of the wrapped auxiliary operators. Previously, it would always wrap them into a dictionary and then fail when unwrapping them later, since it did not preserve the previously wrapped data type.

  • The gathering of the auxiliary operator results when using the VQEClient.

Other Notes#

  • The classes VQEProgram and VQEProgramResult have been removed following the end of the deprecation period.

0.4.0#

New Features#

  • Adds Hartree-Fock (HF) and vibrational self-consistent field (VSCF) initial point classes. HFInitialPoint and VSCFInitialPoint, are to be used with UCC, and UVCC, respectively.

    This follows the introduction of MP2InitialPoint for computing the initial point using the Møller-Plesset 2nd Order (MP2) corrections.

    MP2InitialPoint, HFInitialPoint, and VSCFInitialPoint all inherit from the abstract base class InitialPoint.

    These initial points are intended to be used as starting VQE parameters when using a UCC ansatz (cluster operator coefficients). This should introduce an initial point that is closer to the ground state point, leading to fewer overall evaluations for VQE.

    Instances of the appropriate classes are now the default initial_point arguments in VQEUCCFactory and VQEUVCCFactory. Additionally, one may pass an explicit InitialPoint instance to the factories. Following are some example snippets.

    VQE UCC with a HFInitialPoint:

    hf_intial_point = HFInitialPoint()
    vqe_ucc_factory = VQEUCCFactory(quantum_instance, initial_point=hf_initial_point)
    

    VQE UCC with an MP2InitialPoint:

    mp2_intial_point = MP2InitialPoint()
    vqe_ucc_factory = VQEUCCFactory(your_quantum_instance, initial_point=mp2_initial_point)
    

    VQE UVCC with a VSCFInitialPoint:

    vscf_intial_point = VSCFInitialPoint()
    vqe_uvcc_factory = VQEUVCCFactory(your_quantum_instance, initial_point=vscf_initial_point)
    

    They can also be used by following the snippets below and passing the result via initial_point to the VQE.

    UCC with a HF initial point:

    hf_intial_point = HFInitialPoint()
    hf_initial_point.ansatz = your_ucc_ansatz
    initial_point = hf_initial_point.to_numpy_array()
    

    UVCC with a VSCF initial point:

    vscf_initial_point = VSCFInitialPoint()
    vscf_initial_point.ansatz = your_uvcc_ansatz
    initial_point = vscf_initial_point.to_numpy_array()
    
  • Added logging utility functions accessible through qiskit_nature.logging.

  • Added Support for Bohr unit in Molecule Class:

    mol = Molecule(geometry=[("H", [10.0, 2.0, 0.0]), ("H", [0.0, 20.0, 1.0])], unit=UnitsType.BOHR)
    
  • The new setting qiskit_nature.settings.optimize_einsum was added which allows enabling the optimize argument in numpy.einsum calls with more than 2 operands. This is known to yield significant computational efficiency increases at the expense of higher memory consumption. The setting defaults to True.

  • Adds qiskit_nature.operators.second_quantization.FermionicOp.terms(). This returns an iterable of (term, coefficient) pairs describing the terms contained in the operator. Each term is a list of tuples of the form (action, index), where the action is either “+” or “-” and the index is the integer index of the factor in the term.

  • Added Qiskit Terra’s Gradient Functionality to calculate gradients in AdaptVQE. You can choose the gradient method and input it while calling AdaptVQE. for example:

    from qiskit.providers.basicaer import BasicAer
    from qiskit.utils import QuantumInstance
    from qiskit.opflow.gradients import Gradient
    from qiskit_nature.algorithms import VQEUCCFactory
    from qiskit_nature.drivers import UnitsType
    from qiskit_nature.drivers.second_quantization import PySCFDriver
    from qiskit_nature.mappers.second_quantization import ParityMapper
    from qiskit_nature.converters.second_quantization import QubitConverter
    from qiskit_nature.problems.second_quantization import ElectronicStructureProblem
    from qiskit_nature.algorithms.ground_state_solvers.adapt_vqe import AdaptVQE
    
    driver = PySCFDriver(
          atom="H .0 .0 .0; H .0 .0 0.735", unit=UnitsType.ANGSTROM, basis="sto3g"
          )
    problem = ElectronicStructureProblem(driver)
    qubit_converter = QubitConverter(ParityMapper())
    solver = VQEUCCFactory(QuantumInstance(BasicAer.get_backend("statevector_simulator")))
    grad = Gradient(grad_method="lin_comb", epsilon=1.0)
    calc = AdaptVQE(qubit_converter, solver, gradient=grad)
    res = calc.solve(problem)
    
  • The HDF5Driver has been refactored to leverage the new HDF5-integration protocol of Qiskit Nature. The driver still supports loading legacy QMolecule HDF5 files and also provides a conversion utility:

    driver = HDF5Driver("path_to_qmolecule.hdf5")
    driver.convert(replace=True)
    
  • Adds a new HDF5-integration to support storing and loading of (mostly) Property objects using HDF5 files. A similar feature existed in the legacy QMolecule object but the new implementation is handled more general to enable leveraging this integration throughout more parts of the stack in the future.

    To store a driver result of the new drivers in a file you can do:

    from qiskit_nature.hdf5 import save_to_hdf5
    
    my_driver_result = driver.run()
    save_to_hdf5(my_driver_result, "my_driver_result.hdf5")
    

    and to load it again you would do:

    from qiskit_nature.hdf5 import load_from_hdf5
    
    my_driver_result = load_from_hdf5("my_driver_result.hdf5")
    
  • Support the initialization of FermionicOp with tuple as label. FermionicOp can be initialized using a tuple of integers, (action, index), like below:

    from qiskit_nature.operators.second_quantization import FermionicOp
    
    FermionicOp(
        [([("-", 2), ("+", 1)], 2 + 0j), ([("-", 3)], 34 + 0j)],
        register_length=4,
        display_format="sparse",
    )
    
  • Add the option to initialize a Lattice from a networkx.Graph object, which will be internally converted to a retworkx.PyGraph for performance.

    For example, you can now construct a lattice as

    import networkx as nx
    from qiskit_nature.problems.second_quantization.lattice import Lattice
    
    # 3-regular random graph on 6 nodes
    graph = nx.generators.random_graphs.random_regular_graph(3, n=6)
    lattice = Lattice(graph)
    
  • Adds a new problem class, qiskit_nature.problems.second_quantization.lattice.LatticeModelProblem, for lattice models. It can create second quantized operators from a lattice model. We can use the Ground State Eigensolver with it to calculate the ground state energy.

    from qiskit_nature.problems.second_quantization.lattice import (
      BoundaryCondition, FermiHubbardModel, LineLattice, LatticeModelProblem
    )
    from qiskit.algorithms import NumPyMinimumEigensolver
    from qiskit_nature.algorithms import GroundStateEigensolver
    from qiskit_nature.converters.second_quantization import QubitConverter
    from qiskit_nature.mappers.second_quantization import JordanWignerMapper
    
    solver = NumPyMinimumEigensolver()
    qubit_converter = QubitConverter(JordanWignerMapper())
    calc = GroundStateEigensolver(qubit_converter, solver)
    
    line_lattice = LineLattice(num_nodes=4, boundary_condition=BoundaryCondition.OPEN)
    fhm = FermiHubbardModel.uniform_parameters(
      lattice=line_lattice,
      uniform_interaction=-1.0,
      uniform_onsite_potential=0.0,
      onsite_interaction=5.0,
    )
    lmp = LatticeModelProblem(lattice_model=fhm)
    res = calc.solve(lmp)
    
  • Adds qiskit_nature.mappers.second_quantization.LogarithmicMapper. This class is a mapper for Logarithmic spin-to-qubit mapping. In this local encoding transformation, each individual spin S system is represented via the lowest lying 2S+1 states in a qubit system with the minimal number of qubits needed to represent >= 2S+1 distinct states.

  • Adds a class to compute the Møller-Plesset 2nd Order (MP2) corrections. The corresponding double-excitation coefficients are intended to be used as an initial point for the VQE parameters (cluster operator coefficients) when using a UCC ansatz. This should introduce an initial point that is closer to the ground state point, leading to fewer overall evaluations for VQE.

    MP2InitialPoint inherits from the abstract base class (interface) InitialPoint, which takes a driver result and a UCC ansatz either directly via the compute method or via their respective property setters.

    The MP2 computation requires the grouped_property to contain the ElectronicEnergy, which must contain the two-body molecular orbital matrix and the orbital energies. Optionally, it will also use the Hartree-Fock reference energy to compute the absolute energy.

    When using VQEUCCFactory, an MP2InitialPoint object can be passed via the initial_point keyword argument:

    mp2_initial_point = MP2InitialPoint()
    vqe_ucc_factory = VQEUCCFactory(quantum_instance, initial_point=mp2_initial_point)
    

    In this case the driver result and ansatz will be used to compute the initial point automatically. This will then be passed to the VQE.

    Outside of the factory, one can do this manually:

    mp2_initial_point = MP2InitialPoint()
    mp2_initial_point.compute(driver_result, ansatz)
    initial_point = mp2_initial_point.to_numpy_array()
    
    algorithm = VQE(
      ansatz,
      optimizer=optimizer,
      quantum_instance=quantum_instance,
      initial_point=initial_point
    )
    

    The eventual intention is to retire preferred_init_points from VQE in Terra, so the implementation avoids this property.

  • Improves the performance of the qiskit_nature.properties.second_quantization.electronic.integrals.ElectronicIntegrals.to_second_q_op() method significantly. Previously, generating the second quantized operators for a system size greater than 20 spin orbitals took on the order of minutes to hours (depending on the actual size). Now, even system sizes of 50 spin orbitals can be handled in a matter of seconds.

  • Now there is a function to interpret the raw result of solving a protein folding problem. Previously the problem was encoded in order to be solved more efficiently but the result could not be easily understood by a user. This new functionality decodes the result for the user and provides a plotting of the protein as well as a file with the x,y,z coordinates of each one of the beads in the protein.

    Added: ProteinFoldingResult. This class handles the result found by running a VQE to solve a ProteinFoldingProblem. This class has:

    • Method get_figure() which generates a figure of the shape of the protein.

    • Method save_xyz_file() which saves a .xyz file with the cartesian coordinates of the shape of the molecule.

    • Property protein_shape_file_gen which is an instance of ProteinShapeFileGen. This class has in its turn a method get_xyz_data() which returns an array with the data contained in the .xyz file without having to generate such a file.

    • Property protein_shape_decoder which is an instance of ProteinShapeDecoder. This class has in its turn two properties main_turns and side_turns that return the turn sequences of the main chain and the side chains respectively.

    result = protein_folding_problem.interpret(raw_result)
    # Save xyz file in current directory.
    result.save_xyz_file()
    # Generate plot.
    figure = result.get_figure()
    
  • Adds qiskit_nature.operators.second_quantization.QuadraticHamiltonian. This class is used to represent quadratic fermionic Hamiltonians and includes methods to efficiently diagonalize them.

  • Adds the simplify method to qiskit_nature.operators.second_quantization.SecondQuantizedOp and the normal_ordered method to qiskit_nature.operators.second_quantization.FermionicOp. These methods replace reduce and to_normal_order, which are deprecated. The differences between the new and old methods are the following:

    • simplify does not perform normal-ordering, while reduce does

    • normal_ordered simplifies the result, while to_normal_order does not

  • Adds functionality to prepare Slater determinants and fermionic Gaussian states using the Givens rotation strategy. This functionality is accessed via the following classes:

    • qiskit_nature.circuit.library.SlaterDeterminant

    • qiskit_nature.circuit.library.FermionicGaussianState

  • The solver of BOPESSampler now accepts solvers of type Union[GroundStateSolver,ExcitedStatesSolver] instead of only a GroundStateSolver. This generalizes to excited states the sampling of nuclear coordinates for Born Oppenheimer Potential Energy Surfaces. Adds the get_qubit_operators() in qiskit_nature.algorithms.ExcitedStatesEigensolver. This matches the format of qiskit_nature.algorithms.GroundStateEigensolver. BOPESSampler now also accepts auxiliary operators to pass to the solver. Geometry-dependent observable can also be defined as auxiliaries. See the Sampling the potential energy surface tutorial for a demonstration of how to use this calculation of excited state Born Oppenheimer potential energy surfaces.

  • VQEClient now extends VariationalAlgorithm, meaning users can now implement BOPESSampler using bootstrapping, which works similarly now with VQEClient VQEClient as it did previously with local VQE.

Upgrade Notes#

  • Added support for running with Python 3.10. At the the time of the release, PySCF didn’t have a python 3.10 version.

  • Support for running with Python 3.6 has been removed. To run Nature you need a minimum Python version of 3.7.

  • Changes the default display_format of the constructor of FermionicOp from “dense” to “sparse”.

Deprecation Notes#

  • The delta argument of AdaptVQE is deprecated in favor of supplying a gradient from Qiskit Terra’s qiskit.opflow.gradients framework. In doing so, the default behavior of AdaptVQE changes from using a finite difference scheme for the gradient evaluation to using a parameter shift method. In order to reproduce the original default behavior you must specify delta=1 explicitly or use the following:

    from qiskit.providers.basicaer import BasicAer
    from qiskit.utils import QuantumInstance
    from qiskit.opflow.gradients import Gradient
    from qiskit_nature.algorithms import VQEUCCFactory
    from qiskit_nature.drivers import UnitsType
    from qiskit_nature.drivers.second_quantization import PySCFDriver
    from qiskit_nature.mappers.second_quantization import ParityMapper
    from qiskit_nature.converters.second_quantization import QubitConverter
    from qiskit_nature.problems.second_quantization import ElectronicStructureProblem
    from qiskit_nature.algorithms.ground_state_solvers.adapt_vqe import AdaptVQE
    
    driver = PySCFDriver(
          atom="H .0 .0 .0; H .0 .0 0.735", unit=UnitsType.ANGSTROM, basis="sto3g"
          )
    problem = ElectronicStructureProblem(driver)
    qubit_converter = QubitConverter(ParityMapper())
    solver = VQEUCCFactory(QuantumInstance(BasicAer.get_backend("statevector_simulator")))
    grad = Gradient(grad_method="fin_diff", epsilon=1.0)
    calc = AdaptVQE(qubit_converter, solver, gradient=grad)
    res = calc.solve(problem)
    
  • The following second quantization operator methods are deprecated:

    • qiskit_nature.operators.second_quantization.SecondQuantizedOp.reduce(). Instead, use qiskit_nature.operators.second_quantization.SecondQuantizedOp.simplify().

    • qiskit_nature.operators.second_quantization.FermionicOp.to_normal_order(). Instead, use qiskit_nature.operators.second_quantization.FermionicOp.normal_ordered().

  • The argument gss of the constructor of BOPESSampler was deprecated and replaced by state_solver to match the extension of this class to qiskit_nature.algorithms.ExcitedStatesEigensolver. Now the constructor has the following positional argument:

    • state_solver

  • VQEUCCFactory had setter/getter properties that were repeated with respect to qiskit.algorithms.VQE.

    In order to avoid duplicating all of these attributes, potentially leading to inconsistencies between the attributes of the different classes, these are now deprecated and require you to use the attributes from minimum_eigensolver instead.

    For this reason, the constructor of VQEUCCFactory has been changed as well. Now the constructor only has the following positional arguments:

    • initial_point

    • initial_state

    • ansatz

    Any extra keyword arguments are passed to a constructor of qiskit.algorithms.VQE which will create a VQE that can be accessed via minimum_eigensolver.

    The same changes have been done for VQEUVCCFactory and NumPyMinimumEigensolverFactory

Bug Fixes#

  • The BOPESSampler did not support GroundStateSolvers when built out with MinimumEigensolverFactories. This is fixed, so code like the following now functions correctly:

    solver = GroundStateEigensolver(converter, VQEUCCFactory(quantum_instance))
    sampler = BOPESSampler(solver, bootstrap=True, num_bootstrap=None, extrapolator=None)
    
  • Fix incorrect corner cases in BravyiKitaevSuperFastMapper: Input terms with coefficient zero and output operator equal to zero. Also detect and raise an exception if HartreeFock attempts to use the BKSF mapper.

  • Fix bug in Bravyi-Kitaev Super-Fast (BKSF) Mapper cause by using obsolete method for composing Pauli operators in one kind of fermionic Hamiltonian term.

  • Fixes a bug in VQEClient using dict based aux_operators instead of deprecated list based ones (enabled via qiskit_nature.settings.dict_aux_operators = True).

  • Some minor issues in the ActiveSpaceTransformer were fixed. Namely:

    • iterated properties are no longer deep-copied resulting in non-transformed ones actually being dropped rather than carried on

    • the correct return type object is being constructed and returned

  • Fixes the ability to disable the freeze_core argument of the FreezeCoreTransformer

  • Fixes the wrong inference of register_length in FermionicOp.

  • Fixes the units used for the Molecule generated by the PySCFDriver.

  • Fixes the computation of the spin orbital occupation in a non-singlet state. Previously, a singly occupied MO would result in SO occupations of 0.5 for each spin. Now, the alpha SO will be fully occupied and the beta SO unoccupied.

  • UCC and UVCC when fully configured via the constructor failed to transpile and were not valid circuits. This fixes that to ensure that if UCC / UVCC are fully configured then as circuits they are are immediately valid from the get go.

  • Fix the groundenergy in EigenstateResult to return a correct value.

  • Alter UCC to build the operators on demand when requested via the property, rather than, as before, when the circuit is built. Now if the circuit is built, then the operators, if not built, will be created as before, but since they are cached when built, if done earlier, then these are used. This avoids problems when used in conjunction with VQE that presently fail - most cases today use a fully configured UCC being passed into VQE but whose constructor presently has an unintended side-effect of building the circuit via a logging statement. For other scenarios VQE would fail when it checked on number of qubits and the operators were None, even though UCC was fully configured, when the circuit had not yet been built.

  • Add a type check for the input graphs to Lattice which asserts that the edge weights of the graph are either numeric (or one of None or {} which is replaced by a unit weight). This prevents possibly unexpected errors in the application stack when an operator is constructed from the lattice.

    In particular, the following now raises a ValueError:

    from retworkx import PyGraph
    from qiskit_nature.problems.second_quantization.lattice import Lattice
    
    graph = PyGraph(multigraph=False)
    graph.add_nodes_from(range(3))
    graph.add_edges_from([(0, 1, 1), (1, 2, "banana")])  # banana is not a valid weight!
    
    lattice = Lattice(graph)
    
  • Alter UVCC to build the operators on demand when requested via the property, rather than, as before, when the circuit is built. This is a similar change as was done to UCC.

  • Ensures the VibrationalStructureProblem.truncation_order gets propagated correctly down to the properties before the SecondQuantizedOp instances are constructed.

0.3.0#

New Features#

  • The degree_of_freedom attribute of the Molecule class now has its dedicated getter and setter.

  • Add: qiskit_nature.problems.second_quantization.lattice.models.IsingModel implementing the Hamiltonian of the Ising Model. Add: qiskit_nature.problems.second_quantization.lattice.models.LatticeModel implementing a base class for Lattice models.

  • Add: qiskit_nature.problems.second_quantization.lattice.lattice.Lattice for the generation of general lattices.

    Add: qiskit_nature.problems.second_quantization.lattice.lattice.HyperCubicLattice for the generation of arbitrary d-dimensional lattices.

    Add: qiskit_nature.problems.second_quantization.lattice.lattice.LineLattice for the generation of one-dimensional lattices.

    Add: qiskit_nature.problems.second_quantization.lattice.lattice.SquareLattice for the generation of two-dimensional lattices.

    Add: qiskit_nature.problems.second_quantization.lattice.lattice.TriangularLattice for the generation of two-dimensional lattices with diagonal edges.

    Add: qiskit_nature.problems.second_quantization.lattice.models.FermiHubbardModel implementing the Hamiltonian of the Fermi-Hubbard Model.

  • Exposes the callback option of the VQE algorithm in the VQE factory classes. It also adds a general kwargs dictionary which allows passing any additional arguments to the VQE.

  • Add to_matrix(). This method returns the matrix representation of the operator over the full fermionic Fock space in the occupation number basis.

  • Added Gaussian drivers to allow execution on python 3.9 (MacOS, Windows, Linux).

  • Support was added for generalized fermionic excitations. These kinds of excitations are effectively ignoring the orbital occupancies and instead yield all possible excitations within a spin species. Furthermore, another option was added which can be used to enabled spin- flipped excitations.

  • Runs checks for the excitations in a UCC ansatz when the excitations were created by a function. If the excitations are not in the expected format the checks raise a qiskit_nature.QiskitNatureError.

Upgrade Notes#

  • In order to fix the ElectronicStructureProblem symmetry_sector_locator() information on the mapping was required and the QubitConverter sector_locator parameter callback signature of the convert() method was changed from sector_locator: Optional[Callable[[Z2Symmetries], Optional[List[int]]]] to sector_locator: Optional[Callable[[Z2Symmetries, "QubitConverter"], Optional[List[int]]]] i.e. your supplied callback method must now take a second parameter which is a QubitConverter instance and when invoked will be the instance upon which the convert was called. If you have created your own sector locator then you will need to update it`s signature otherwise the code will fail when calling it now.

  • The EvolvedOperatorAnsatz, from the Nature circuit.library, which was migrated in an earlier release to core Qiskit i.e. Terra, has now been removed. You should change any code that still uses this over to the core Qiskit one, from qiskit.circuit.library, as a direct replacement.

Deprecation Notes#

  • Rename the runtime “program” to runtime “client” to avoid name confusions and reflect the fact that it is an interface for code executed in the cloud. The classes VQEProgram and VQEProgramResult have been renamed to VQEClient, VQERuntimeResult, respectively.

Bug Fixes#

  • This also ensures that the getter/setter methods of the VQE factories actually affect the constructed VQE instance.

  • Fixed the manual active orbital selection when specifying the active number of electrons as a tuple rather than an integer.

  • Fixed a typo in the ElectronicEnergy._orbital_energies variable name and ensures the correct behavior of the orbital_energies property getter.

  • The ElectronicStructureResult did not initialize the _computed_dipole_moment variable causing critical errors when trying to print a result from an algorithm which does not compute these observables. Proper initialization fixes this issue. Printing the result would also fail when complex numbers were stored. This has also been remedied.

  • Fixed an issue where the existing Mapper was incorrectly mapping the creation operator \(+\) and the annihilation operator \(-\). It used to be that \(+\) was mapped to \(\sigma_+\) and \(-\) was mapped to \(\sigma_-\), but it is correct that \(+\) is mapped to \(\sigma_-\) and \(-\) is mapped to :math`sigma_+`.

  • Fixes the creation on the HartreeFock initial state which could fail when Z2Symettry reduction was being used via the provided QubitConverter.

    Also fixes the ElectronicStructureProblem symmetry_sector_locator(), which uses the HartreeFock class too, so that it correctly determines the sector containing the ground state when auto symmetry reduction is used.

  • Ensure BaseProblem.molecule_data_transformed is set when using a legacy driver type without any transformers.

  • Fixes the formatting of the occupied modals and adds the excited state energies when printing a VibrationalStructureResult.

    Fixes the return type of num_occupied_modals_per_mode() from Optional[List[float]] to Optional[List[List[float]]]

  • Fixes QEOM such that when using parity mapping with two_qubit_reduction, or Z2 symmetries with any mapping, that the excited states are computed as expected.

    Fix the electronic structure problem sector locator such that the ‘auto’ Z2 symmetry conversion, of the qubit converter, results in the ground state for such problems and not some other value due to incorrect sector selection.

  • Allow input operator to BravyiKitaevSuperFastMapper to be FermionicOp in sparse storage format. Previously, all input was interpreted as dense storage format, which raised an error when parsing sparse format as dense failed.

  • Updates the runtime tutorial to consistently use the new drivers in combination with the new transformers and the Property-framework in general.

0.2.0#

New Features#

  • Add qiskit_nature.mappers.second_quantization.BravyiKitaevSuperFastMapper implementing the Bravyi-Kitaev super-fast fermion-to-qubit mapping. For example

    from qiskit_nature.mappers.second_quantization import BravyiKitaevSuperFastMapper
    mapper = BravyiKitaevSuperFastMapper()
    mapper.map(fermionic_op)
    
  • The SecondQuantizedOp now has a method is_hermitian() method which will return True if the operator is equivalent to its adjoint and False otherwise

  • to_list() is now an abstract method of SecondQuantizedOp that is implemented by all subclasses

  • Add to_normal_order(). It returns the normal ordered fermionic operator that is equivalent to self.

  • Introduces the ElectronicStructureMoleculeDriver and VibrationalStructureMoleculeDriver that allow for the creation of Molecule-based drivers by specifying a molecule plus basis and method (for Electronic Structure drivers only) and a driver type. An additional type AUTO allows for the lookup of the first driver installed that supports the given method. The documentation of those two molecule driver classes gives more details on it.

  • Introduce the VQEProgram to allow leveraging Qiskit Runtime to speed up the VQE algorithm. The VQEProgram implements the MinimumEigensolver interface and can thus be used as a drop-in replacement for other minimum eigensolvers like qiskit.algorithms.VQE. See the tutorials under docs/tutorials for an explicit example usage.

Upgrade Notes#

  • The internal data in FermionicOp have been changed. As a result, more data type is now accepted by initialize. The ascending order constraint and the no-same index constraint have been removed. In addition, the dense and sparse labels are now automatically detected by the existence of underscores.

    The property display_format of FermionicOp is added. There are two modes dense and sparse. This display format can be switched by the property FermionicOp.display_format.

  • The internal API of the abstract class qiskit_nature.operators.second_quantization.SecondQuantizedOp abstract class has been changed to use StarAlgebraMixin.

Deprecation Notes#

  • The property dagger in the second quantized operators is deprecated. Use adjoint() method alternatively.

    • the legacy driver return types, QMolecule and WatsonHamiltonian

    • the legacy transformers acting on the now deprecated driver return types

    • the BaseProblem.molecule_data and BaseProblem.molecule_data_transformed attributes

  • All currently existing drivers have been moved from qiskit_nature.drivers to qiskit_nature.drivers.second_quantization. This is necessary because future additions to Nature which reside in parallel to the second_quantization submodules will not be using these drivers. Making this separation reflects that in the code structure. The same change was necessary for the existing qiskit_nature.transformers.

Bug Fixes#

  • Fixed an issue where FermionicOp raises unwanted ValueError when initialized with some list of sparse label.

  • Fixes the issue #198 where total dipole moment was not calculated correctly in the ElectronicStructureResult.

  • QiskitNatureError``s where not being raised properly by the ``ActiveSpaceTransformer due to ill-formatted error messages.

  • Fix AdaptVQE after the update of VQE which deleted the internal _energy_evaluation method that Adapt-VQE was relying on.

  • The FreezeCoreTransformer (and ActiveSpaceTransformer) were incompatible with the automatic Z2Symmetry reduction. This issue was fixed by correcting the ElectronicStructureProblem.symmetry_sector_locator method.

  • The two-qubit reduction needs to be skipped when a qubit operator only has 2 (or even fewer) qubits.

  • The VQEProgram does support the evaluation of auxiliary operators at the final state, but the qiskit_nature.runtime.VQEProgram.supports_aux_operators() method previously returned False instead of True.

  • Allow Qiskit’s Optimizer classes as input for the optimizer in the VQEProgram instead of only dictionaries.

Other Notes#

  • Changed documentation and code to better reflect rebranding of Qiskit’s chemistry module as Qiskit Nature.