Skip to content

Commit

Permalink
Add PolynomialTensor class (qiskit-community#756)
Browse files Browse the repository at this point in the history
* adding PolynomialTensor class

* adding unittest corresponding PolynomialTensor class; making changes to the class as suggested

* adding conjugate and transpose functions

* made changes to polynomial_tensor and test_polyomial_tensor files

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* updating polynomial_tensor and unittest

* updating polynomial tensor and unittest

* update unittest

* updating polynomial_tensor file

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* update polynomial_tensor and test_polynomial_tensor

* adding register_length to polynomial tensor

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* changes to polynomial_tensor - adding register_length

* adding equiv method to polynomial_tensor. adding unittests for dunders

* lint and mypy update

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* updating polynomial_tensor

* Update qiskit_nature/second_q/operators/polynomial_tensor.py

Co-authored-by: Max Rossmannek <[email protected]>

* update polynomial_tensor

Co-authored-by: Manoel Marques <[email protected]>
Co-authored-by: Max Rossmannek <[email protected]>
Co-authored-by: Steve Wood <[email protected]>
Co-authored-by: Max Rossmannek <[email protected]>
  • Loading branch information
5 people authored Sep 14, 2022
1 parent e74ca18 commit 77835fd
Show file tree
Hide file tree
Showing 4 changed files with 392 additions and 0 deletions.
1 change: 1 addition & 0 deletions .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ physrevd
physik
plesset
pmatrix
polynomialtensor
polypeptides
popovas
pos
Expand Down
3 changes: 3 additions & 0 deletions qiskit_nature/second_q/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@
SpinOp
SecondQuantizedOp
VibrationalOp
PolynomialTensor
"""

from .fermionic_op import FermionicOp
from .second_quantized_op import SecondQuantizedOp
from .spin_op import SpinOp
from .vibrational_op import VibrationalOp
from .polynomial_tensor import PolynomialTensor
from .sparse_label_op import SparseLabelOp

__all__ = [
"FermionicOp",
"SecondQuantizedOp",
"SpinOp",
"VibrationalOp",
"PolynomialTensor",
"SparseLabelOp",
]
208 changes: 208 additions & 0 deletions qiskit_nature/second_q/operators/polynomial_tensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""PolynomialTensor class"""

from __future__ import annotations
from typing import Dict, Iterator
from collections.abc import Mapping
from numbers import Number
import numpy as np
from qiskit.quantum_info.operators.mixins import (
LinearMixin,
AdjointMixin,
TolerancesMixin,
)


class PolynomialTensor(LinearMixin, AdjointMixin, TolerancesMixin, Mapping):
"""PolynomialTensor class"""

def __init__(self, data: Mapping[str, np.ndarray | Number], register_length: int) -> None:
"""
Args:
data: mapping of string-based operator keys to coefficient matrix values.
register_length: dimensions of the value matrices in data mapping.
Raises:
ValueError: when length of operator key does not match dimensions of value matrix.
ValueError: when value matrix does not have consistent dimensions.
ValueError: when some or all value matrices in ``data`` have different dimensions.
"""
copy_dict: Dict[str, np.ndarray] = {}

for key, value in data.items():
if isinstance(value, Number):
value = np.asarray(value)

if len(value.shape) != len(key):
raise ValueError(
f"Data key {key} of length {len(key)} does not match "
f"data value matrix of dimensions {value.shape}"
)

dims = set(value.shape)

if len(dims) > 1:
raise ValueError(
f"For key {key}: dimensions of value matrix are not identical {value.shape}"
)
if len(dims) == 1 and dims.pop() != register_length:
raise ValueError(
f"Dimensions of value matrices in data dictionary do not match the provided "
f"register length, {register_length}"
)

copy_dict[key] = value

self._data = copy_dict
self._register_length = register_length

@property
def register_length(self) -> int:
"""Returns register length of the operator key in `PolynomialTensor`."""
return self._register_length

def __getitem__(self, __k: str) -> (np.ndarray | Number):
"""
Returns value matrix in the `PolynomialTensor`.
Args:
__k: operator key string in the `PolynomialTensor`.
Returns:
Value matrix corresponding to the operator key `__k`
"""
return self._data.__getitem__(__k)

def __len__(self) -> int:
"""
Returns length of `PolynomialTensor`.
"""
return self._data.__len__()

def __iter__(self) -> Iterator[str]:
"""
Returns iterator of the `PolynomialTensor`.
"""
return self._data.__iter__()

def _multiply(self, other: complex) -> PolynomialTensor:
"""Scalar multiplication of PolynomialTensor with complex
Args:
other: scalar to be multiplied with the ``PolynomialTensor``.
Returns:
the new ``PolynomialTensor`` product object.
Raises:
TypeError: if ``other`` is not a ``Number``.
"""
if not isinstance(other, Number):
raise TypeError(f"other {other} must be a number")

prod_dict: Dict[str, np.ndarray] = {}
for key, matrix in self._data.items():
prod_dict[key] = np.multiply(matrix, other)
return PolynomialTensor(prod_dict, self._register_length)

def _add(self, other: PolynomialTensor, qargs=None) -> PolynomialTensor:
"""Addition of PolynomialTensors
Args:
other: second``PolynomialTensor`` object to be added to the first.
Returns:
the new summed ``PolynomialTensor``.
Raises:
TypeError: when ``other`` is not a ``PolynomialTensor``.
ValueError: when values corresponding to keys in ``other`` and
the first ``PolynomialTensor`` object do not match.
"""
if not isinstance(other, PolynomialTensor):
raise TypeError("Incorrect argument type: other should be PolynomialTensor")

if self._register_length != other._register_length:
raise ValueError(
"The dimensions of the PolynomialTensors which are to be added together, do not "
f"match: {self._register_length} != {other._register_length}"
)

sum_dict = {key: value + other._data.get(key, 0) for key, value in self._data.items()}
other_unique = {key: other._data[key] for key in other._data.keys() - self._data.keys()}
sum_dict.update(other_unique)

return PolynomialTensor(sum_dict, self._register_length)

def __eq__(self, other: object) -> bool:
"""Check equality of first PolynomialTensor with other
Args:
other: second``PolynomialTensor`` object to be compared with the first.
Returns:
True when ``PolynomialTensor`` objects are equal, False when unequal.
"""
if not isinstance(other, PolynomialTensor):
return False

if self._register_length != other._register_length:
return False

if self._data.keys() != other._data.keys():
return False

for key, value in self._data.items():
if not np.array_equal(value, other._data[key]):
return False
return True

def equiv(self, other: object) -> bool:
"""Check equivalence of first PolynomialTensor with other
Args:
other: second``PolynomialTensor`` object to be compared with the first.
Returns:
True when ``PolynomialTensor`` objects are equivalent, False when not.
"""
if not isinstance(other, PolynomialTensor):
return False

if self._register_length != other._register_length:
return False

if self._data.keys() != other._data.keys():
return False

for key, value in self._data.items():
if not np.allclose(value, other._data[key], atol=self.atol, rtol=self.rtol):
return False
return True

def conjugate(self) -> PolynomialTensor:
"""Conjugate of PolynomialTensors
Returns:
the complex conjugate of the ``PolynomialTensor``.
"""
conj_dict: Dict[str, np.ndarray] = {}
for key, value in self._data.items():
conj_dict[key] = np.conjugate(value)

return PolynomialTensor(conj_dict, self._register_length)

def transpose(self) -> PolynomialTensor:
"""Transpose of PolynomialTensor
Returns:
the transpose of the ``PolynomialTensor``.
"""
transpose_dict: Dict[str, np.ndarray] = {}
for key, value in self._data.items():
transpose_dict[key] = np.transpose(value)

return PolynomialTensor(transpose_dict, self._register_length)
Loading

0 comments on commit 77835fd

Please sign in to comment.