Tensor#

class Tensor(array, *, label_template=None)[fuente]#

Bases: NDArrayOperatorsMixin, TolerancesMixin

A tensor representation wrapping single numeric values, dense or sparse arrays.

This class is designed to unify the usage of tensors throughout the stack. It provides a central entry point for the handling of arrays enabling seamless interchange of dense and sparse arrays in any use-case. This is done by implementing this class as an np.ndarray container as described here. At the same time this class can also wrap a sparse.SparseArray (which in turn implements the np.ndarray interface).

Parámetros:
  • array (Number | ARRAY_TYPE | Tensor) –

    the wrapped array object. This can be any of the following objects:

    numpy.ndarray:

    a dense matrix

    sparse.SparseArray:

    a sparse matrix

    numbers.Number:

    any numeric singular value

    Tensor:

    another Tensor whose array attribute will be extracted

  • label_template (str | None) – the template string used during the translation procedure implemented in SparseLabelOp.from_polynomial_tensor(). When None, this will fall back to the default template string - see the label_template property for more information.

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

Nota

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)

Nota

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)[fuente]#

Returns the matrix multiplication with another Tensor.

Parámetros:
  • other (Tensor) – the other Tensor.

  • qargs (None) – UNUSED.

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

Devuelve:

The tensor resulting from the composition.

Muestra:

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

Tipo del valor devuelto:

Tensor

coord_iter()[fuente]#

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)
Campos:

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.

Tipo del valor devuelto:

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

equiv(other)[fuente]#

Check equivalence of Tensor instances.

Nota

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.

Parámetros:

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

Devuelve:

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

Tipo del valor devuelto:

bool

expand(other)[fuente]#

Returns the reverse-order tensor product with another Tensor.

Parámetros:

other (Tensor) – the other Tensor.

Devuelve:

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

Muestra:

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

Tipo del valor devuelto:

Tensor

Nota

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

is_dense()[fuente]#

Returns whether this tensor is dense.

Tipo del valor devuelto:

bool

is_sparse()[fuente]#

Returns whether this tensor is sparse.

Tipo del valor devuelto:

bool

tensor(other)[fuente]#

Returns the tensor product with another Tensor.

Parámetros:

other (Tensor) – the other Tensor.

Devuelve:

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

Muestra:

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

Tipo del valor devuelto:

Tensor

Nota

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

to_dense()[fuente]#

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.

Tipo del valor devuelto:

Tensor

to_sparse(*, sparse_type=<class 'qiskit_nature.second_q.operators.tensor.COO'>)[fuente]#

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.

Parámetros:
  • 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) –

Devuelve:

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

Tipo del valor devuelto:

Tensor