SymmetricTwoBodyIntegrals#

class SymmetricTwoBodyIntegrals(eri, *, validate=True)[source]#

Bases: Tensor, ABC

An abstract base class providing the interface for symmetry-reduced two-body electronic integral container classes.

Parameters:
  • eri (Tensor | ARRAY_TYPE) – the actual two-body tensor.

  • validate (bool) – when set to False, the requirements of eri are not validated.

Note

Actual implementations of this abstract base class may raise a ValueError if eri does not fulfill the requirements imposed by that subclass.

Attributes

array#

Returns the wrapped array object.

atol = 1e-08#
label_template#

The template string used during the translation implemented in SparseLabelOp.from_polynomial_tensor().

If the label_template is set to None (the default value) during initialization of a Tensor instance, this value will be substituted by the internal default template. Its value depends on the dimension of the wrapped matrix: it will repeat {}_{{}} for every dimension (independent of its size). This is explained best with an example:

print(Tensor(np.eye(4)).label_template)
# "{}_{{}} {}_{{}}"

print(Tensor(np.ones((3, 1, 2)).label_template)
# "{}_{{}} {}_{{}} {}_{{}}"

print(Tensor(np.ones((2, 2, 2, 2)).label_template)
# "{}_{{}} {}_{{}} {}_{{}} {}_{{}}"

The format of this template allows to construct a SparseLabelOp from the Tensors stored in a PolynomialTensor. This operation is performed when calling SparseLabelOp.from_polynomial_tensor(). There, the template is processed using the Python string formatter in two steps:

  1. First, the template is formatted using the key under which the Tensor object was stored inside of the PolynomialTensor object. For example:

     poly = PolynomialTensor(
        {
            "+-": Tensor(np.eye(2)),
            "++--": Tensor(np.ones((2, 2, 2, 2))),
        }
    )
    
     # the label_template will get expanded like so:
     for key, tensor in poly.items():
        sparse_label_template = tensor.label_template.format(*key)
        print(key, "->", sparse_label_template)
    
    # "+-" -> "+_{} -_{}"
    # "++--" -> "+_{} +_{} -_{} -_{}"
    
  2. Next, these templates are used to build the actual labels of the SparseLabelOp being constructed. For that, the indices encountered during coord_iter() are used for example like so:

    sparse_label_template = "+_{} -_{}"
    for value, index in Tensor(np.eye(2)).coord_iter():
        sparse_label = sparse_label_template.format(*index)
        print(sparse_label, value)
    
    # "+_0 -_0", 1
    # "+_0 -_1", 0
    # "+_1 -_1", 1
    # "+_1 -_0", 0
    

Given that you now understand how the label_template attribute is being used, this allows you to modify how the Tensor objects stored inside a PolynomialTensor are processed when they are being translated into a SparseLabelOp.

Here is a concrete example which enables you to use chemistry-ordered two-body terms:

eri_chem = ...  # chemistry-ordered 2-body integrals (a 4-dimensional array)
tensor = Tensor(eri_chem)
tensor.label_template = "+_{{0}} +_{{2}} -_{{3}} -_{{1}}"
poly = PolynomialTensor({"++--": tensor})
ferm_op_chem = FermionicOp.from_polynomial_tensor(poly)

# ferm_op_chem is now identical to the following:
from qiskit_nature.second_q.operators.tensor_ordering import to_physicist_ordering

eri_phys = to_physicist_ordering(eri_chem)
poly = PolynomialTensor({"++--": eri_phys})
ferm_op_phys = FermionicOp.from_polynomial_tensor(poly)

print(ferm_op_chem.equiv(ferm_op_phys))  # True

Note

The string formatting in Python is a very powerful tool [1]. Note, that in the use case here, we only ever supply positional arguments in the .format(...) calls which means that you cannot use names to identify replacement fields in your label templates. However, you can modify their replacement order using numeric values (like shown below).

Another detail to keep in mind, is that the number of replacement fields may _not_ exceed the number of arguments provided to the .format(...) call. However, the number of arguments _can_ exceed the number of replacement fields in your template (this will not cause any errors).

Both of those noted features were actually used in the example provided above:

  1. a custom ordering of the replacement fields in our template string

  2. a smaller number of replacement fields than arguments (because we already hard-coded the + and - operator strings such that the first expansion to the sparse_label_template only unpacks one set of curly braces but does not actually inject anything into the template)

Note

You could have also used the following template: {}_{{0}} {}_{{2}} {}_{{3}} {}_{{1}}. This will work in the same way if the key under which your Tensor is stored inside of the PolynomialTensor is ++--. We did not do this in the example above to show that the number of replacement fields can be smaller than the number of arguments provided during the formatting step, and to simplify the example a little bit.

However, if you were to try to use +_{0} +_{2} -_{3} -_{1} instead, this will not work as intended because the both string formatting steps are applied unconditionally! Thus, this wrong use case would in fact get expanded to +_+ +_- -_- -_+ in the first step of the processing leaving no replacement fields to be processed in the second step.

ndim#

Returns the number of dimensions of the wrapped array object.

rtol = 1e-05#
shape#

Returns the shape of the wrapped array object.

Methods

compose(other, qargs=None, front=False)#

Returns the matrix multiplication with another Tensor.

Parameters:
  • other (Tensor) – the other Tensor.

  • qargs (None) – UNUSED.

  • front (bool) – If True, composition uses right matrix multiplication, otherwise left multiplication is used (the default).

Returns:

The tensor resulting from the composition.

Raises:

NotImplementedError – when composing Tensor instances whose label_template attributes are not falling back to the default.

Return type:

Tensor

conjugate()[source]#

Returns the complex conjugate of itself.

Return type:

SymmetricTwoBodyIntegrals

coord_iter()#

Iterates a matrix yielding pairs of values and their coordinates.

This is best explained with a simple example:

for value, index in Tensor(np.arange(4).reshape((2, 2))).coord_iter():
    print(value, index)

# 0 (0, 0)
# 1 (0, 1)
# 2 (1, 0)
# 3 (1, 1)
Yields:

A tuple containing the matrix value and another tuple of integers indicating the “coordinate” (or multi-dimensional index) under which said value can be found.

Return type:

Generator[tuple[Number, tuple[int, …]], None, None]

equiv(other)#

Check equivalence of Tensor instances.

Note

This check only asserts the internal matrix elements for equivalence but ignores the type of the matrices. As such, it will indicate equivalence of two Tensor instances even if one contains sparse and the other dense numpy arrays, as long as their elements match.

Parameters:

other (object) – the second Tensor object to be compared with the first.

Returns:

True when the Tensor objects are equivalent, False when not.

Return type:

bool

expand(other)#

Returns the reverse-order tensor product with another Tensor.

Parameters:

other (Tensor) – the other Tensor.

Returns:

The tensor resulting from the tensor product, \(other \otimes self\).

Raises:

NotImplementedError – when expanding Tensor instances whose label_template attributes are not falling back to the default.

Return type:

Tensor

Note

Expand uses reversed operator ordering to tensor(). For two tensors of the same type a.expand(b) = b.tensor(a).

is_dense()#

Returns whether this tensor is dense.

Return type:

bool

is_sparse()#

Returns whether this tensor is sparse.

Return type:

bool

tensor(other)#

Returns the tensor product with another Tensor.

Parameters:

other (Tensor) – the other Tensor.

Returns:

The tensor resulting from the tensor product, \(self \otimes other\).

Raises:

NotImplementedError – when tensoring Tensor instances whose label_template attributes are not falling back to the default.

Return type:

Tensor

Note

Tensor uses reversed operator ordering to expand(). For two tensors of the same type a.tensor(b) = b.expand(a).

to_dense()#

Returns a new instance with the internal array converted to a dense numpy array.

If the instance on which this method was called already fulfilled this requirement, it is returned unchanged.

Return type:

Tensor

to_sparse(*, sparse_type=<class 'sparse._coo.core.COO'>)#

Returns a new instance with the internal array converted to a sparse array.

If the instance on which this method was called already fulfilled this requirement, it is returned unchanged.

Parameters:
  • sparse_type (Type[COO] | Type[DOK] | Type[GCXS]) – the type to use for the conversion to sparse array. Note, that this will

  • sparse (only be applied if the wrapped array object was dense. Converting an already) –

  • explicitly. (array to another sparse type needs to be done) –

Returns:

A new Tensor with the internal array converted to the requested sparse array type.

Return type:

Tensor

abstract classmethod zero(norb)[source]#

Constructs an all-zero integral container of the requested size.

Parameters:

norb (int) – the number of orbitals indicating the dimension of the integral container to be returned.

Returns:

An integral container of the requested size with all-zero terms.

Return type:

SymmetricTwoBodyIntegrals