Skip to content

Commit

Permalink
Black PEP 8.
Browse files Browse the repository at this point in the history
  • Loading branch information
AGaliciaMartinez committed Jul 11, 2021
1 parent 1658d8e commit a361661
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 66 deletions.
2 changes: 1 addition & 1 deletion src/qutip_tensorflow/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__all__ = ['data']
__all__ = ["data"]
43 changes: 26 additions & 17 deletions src/qutip_tensorflow/core/data/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,43 @@
warnings.filterwarnings("ignore", category=DeprecationWarning)
from tensorflow.errors import InvalidArgumentError

__all__ = ['add_DenseTensor', 'sub_DenseTensor']
__all__ = ["add_DenseTensor", "sub_DenseTensor"]

# Conversion function
def add_DenseTensor(left, right, scale=1):
if left.shape != right.shape:
raise ValueError(f"""Incompatible shapes for adition of two matrices:
left={left.shape} and right={right.shape}""")
raise ValueError(
f"""Incompatible shapes for adition of two matrices:
left={left.shape} and right={right.shape}"""
)

# If scale=1 we obtain a x2 speed-up if we do not multiply by the scale.
if scale==1:
return DenseTensor._fast_constructor(left._tf + right._tf,
shape=left.shape)
if scale == 1:
return DenseTensor._fast_constructor(left._tf + right._tf, shape=left.shape)
else:
return DenseTensor._fast_constructor(left._tf + scale*right._tf,
shape=left.shape)
return DenseTensor._fast_constructor(
left._tf + scale * right._tf, shape=left.shape
)


def sub_DenseTensor(left, right):
if left.shape != right.shape:
raise ValueError(f"""Incompatible shapes for adition of two matrices:
left={left.shape} and right={right.shape}""")
raise ValueError(
f"""Incompatible shapes for adition of two matrices:
left={left.shape} and right={right.shape}"""
)
return DenseTensor(left._tf - right._tf, shape=left.shape, copy=False)

# `add_conversions` will register the data layer
qutip.data.add.add_specialisations([
(DenseTensor, DenseTensor, DenseTensor, add_DenseTensor),
])

qutip.data.sub.add_specialisations([
(DenseTensor, DenseTensor, DenseTensor, sub_DenseTensor),
])
# `add_conversions` will register the data layer
qutip.data.add.add_specialisations(
[
(DenseTensor, DenseTensor, DenseTensor, add_DenseTensor),
]
)

qutip.data.sub.add_specialisations(
[
(DenseTensor, DenseTensor, DenseTensor, sub_DenseTensor),
]
)
1 change: 1 addition & 0 deletions src/qutip_tensorflow/core/data/dense_tensor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import warnings
import qutip
import numbers

qutip.settings.core["auto_tidyup"] = False

with warnings.catch_warnings():
Expand Down
11 changes: 8 additions & 3 deletions tests/core/data/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
warnings.filterwarnings("ignore", category=DeprecationWarning)
import tensorflow as tf


def shuffle_indices_scipy_csr(matrix):
"""
Given a scipy.sparse.csr_matrix, shuffle the indices within each row and
Expand All @@ -28,18 +29,19 @@ def shuffle_indices_scipy_csr(matrix):
order = np.argsort(np.random.rand(ptr[1] - ptr[0]))
# If sorted, reverse it.
order = np.flip(order) if np.all(order[:-1] < order[1:]) else order
out.indices[ptr[0]:ptr[1]] = out.indices[ptr[0]:ptr[1]][order]
out.data[ptr[0]:ptr[1]] = out.data[ptr[0]:ptr[1]][order]
out.indices[ptr[0] : ptr[1]] = out.indices[ptr[0] : ptr[1]][order]
out.data[ptr[0] : ptr[1]] = out.data[ptr[0] : ptr[1]][order]
return out


def random_scipy_csr(shape, density, sorted_):
"""
Generate a random scipy CSR matrix with the given shape, nnz density, and
with indices that are either sorted or unsorted. The nnz elements will
always be at least one.
"""
nnz = int(shape[0] * shape[1] * density) or 1
data = np.random.rand(nnz) + 1j*np.random.rand(nnz)
data = np.random.rand(nnz) + 1j * np.random.rand(nnz)
rows = np.random.choice(np.arange(shape[0]), nnz)
cols = np.random.choice(np.arange(shape[1]), nnz)
sci = scipy.sparse.coo_matrix((data, (rows, cols)), shape=shape).tocsr()
Expand All @@ -62,14 +64,17 @@ def random_tensor_dense(shape):
out = tf.constant(out)
return out


def random_DenseTensor(shape):
"""Generate a random DenseTensor matrix with the given shape."""
return DenseTensor(random_tensor_dense(shape))


def random_dense(shape, fortran):
"""Generate a random qutip Dense matrix of the given shape."""
return qutip.core.data.Dense(random_numpy_dense(shape, fortran))


def random_csr(shape, density, sorted_):
"""
Generate a random qutip CSR matrix with the given shape, nnz density, and
Expand Down
94 changes: 52 additions & 42 deletions tests/core/data/test_mathematics.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# First set up a bunch of allowable shapes, for different types of functions so
# we don't have to respecify a whole lot of things on repeat.


def shapes_unary(dim=100):
"""Base shapes to test for unary functions."""
# Be sure to test a full spectrum bra-type, ket-type and square and
Expand Down Expand Up @@ -117,10 +118,8 @@ def shapes_binary_bad_matmul(dim=100):
# getting just a single case from each.
_ALL_CASES = {
DenseTensor: lambda shape: [lambda: conftest.random_DenseTensor(shape)],
}
_RANDOM = {
DenseTensor: lambda shape: [lambda: conftest.random_DenseTensor(shape)]
}
_RANDOM = {DenseTensor: lambda shape: [lambda: conftest.random_DenseTensor(shape)]}


def cases_type_shape_product(cases_lookup, op, types, shapes, out_type=None):
Expand Down Expand Up @@ -177,6 +176,7 @@ def cases_type_shape_product(cases_lookup, op, types, shapes, out_type=None):
shape. `out_type` is present in the output only if it were given as a
parameter itself.
"""

def case(type_, shape_case, generator_case):
"""
Build a case parameter for _one_ generator function which will return
Expand All @@ -185,7 +185,7 @@ def case(type_, shape_case, generator_case):
id_ = type_.__name__
inner = ""
for extra in [shape_case, generator_case]:
if hasattr(extra, 'id') and extra.id:
if hasattr(extra, "id") and extra.id:
inner += ("," if inner else "") + extra.id
if inner:
id_ += "[" + inner + "]"
Expand All @@ -201,8 +201,10 @@ def case(type_, shape_case, generator_case):
# Convert the list of types into a list of lists of the special cases
# needed for each type.
matrix_cases = [
[case(type_, shape_case, type_case)
for type_case in cases_lookup[type_](shape_case.values[0])]
[
case(type_, shape_case, type_case)
for type_case in cases_lookup[type_](shape_case.values[0])
]
for type_, shape_case in zip(types, shapes_)
]
# Now Cartesian product all the special cases together to make the full
Expand All @@ -227,6 +229,7 @@ def case(type_, shape_case, generator_case):
# `pytest_generate_tests` in order to generate all the parametrisations for the
# given test.


class _GenericOpMixin:
"""
Abstract base mix-in which sets up the test generation for the two basic
Expand Down Expand Up @@ -268,7 +271,10 @@ class _GenericOpMixin:
add(CSR, Dense) -> Other
would be specified as `(add, CSR, Dense, Other)`.
"""
def op_numpy(self, *args): raise NotImplementedError

def op_numpy(self, *args):
raise NotImplementedError

# With dimensions of around 100, we have to account for floating-point
# addition not being associative; the maths on full numpy arrays will often
# produce slightly different results to sparse algebra, since the order of
Expand All @@ -280,10 +286,9 @@ def op_numpy(self, *args): raise NotImplementedError

def generate_mathematically_correct(self, metafunc):
parameters = (
['op']
+ [x for x in metafunc.fixturenames
if x.startswith("data_")]
+ ['out_type']
["op"]
+ [x for x in metafunc.fixturenames if x.startswith("data_")]
+ ["out_type"]
)
cases = []
for p_op in self.specialisations:
Expand All @@ -293,21 +298,23 @@ def generate_mathematically_correct(self, metafunc):
metafunc.parametrize(parameters, cases)

def generate_incorrect_shape_raises(self, metafunc):
parameters = (
['op']
+ [x for x in metafunc.fixturenames
if x.startswith("data_")]
)
parameters = ["op"] + [
x for x in metafunc.fixturenames if x.startswith("data_")
]
if not self.bad_shapes:
reason = "".join([
"no shapes are 'incorrect' for ",
metafunc.cls.__name__,
"::",
metafunc.function.__name__,
])
false_case = pytest.param(*([None]*len(parameters)),
marks=pytest.mark.skip(reason),
id="no test")
reason = "".join(
[
"no shapes are 'incorrect' for ",
metafunc.cls.__name__,
"::",
metafunc.function.__name__,
]
)
false_case = pytest.param(
*([None] * len(parameters)),
marks=pytest.mark.skip(reason),
id="no test"
)
metafunc.parametrize(parameters, [false_case])
return
cases = []
Expand All @@ -324,10 +331,7 @@ def pytest_generate_tests(self, metafunc):
# down on boilerplate, but also that derived classes _may_ override the
# generation of tests defined in a base class, say if they have
# additional special arguments that need parametrising over.
generator_name = (
"generate_"
+ metafunc.function.__name__.replace("test_", "")
)
generator_name = "generate_" + metafunc.function.__name__.replace("test_", "")
try:
generator = getattr(self, generator_name)
except AttributeError:
Expand All @@ -341,6 +345,7 @@ class UnaryOpMixin(_GenericOpMixin):
negation). Only generates the test `mathematically_correct`, since there
can't be a shape mismatch when there's only one argument.
"""

shapes = [(x,) for x in shapes_unary()]

def test_mathematically_correct(self, op, data_m, out_type):
Expand All @@ -354,20 +359,25 @@ def test_mathematically_correct(self, op, data_m, out_type):
else:
assert abs(test - expected) < self.tol


class UnaryScalarOpMixin(_GenericOpMixin):
"""
Mix-in for unary mathematical operations on Data instances, but that also
take in a numeric scalar (e.g. scalar multiplication). Only generates
the test `mathematically_correct`, since there can't be a shape mismatch
when there's only one Data argument.
"""

shapes = [(x,) for x in shapes_unary()]

@pytest.mark.parametrize('scalar', [
pytest.param(0, id='zero'),
pytest.param(4.5, id='real'),
pytest.param(3j, id='complex'),
])
@pytest.mark.parametrize(
"scalar",
[
pytest.param(0, id="zero"),
pytest.param(4.5, id="real"),
pytest.param(3j, id="complex"),
],
)
def test_mathematically_correct(self, op, data_m, scalar, out_type):
matrix = data_m()
expected = self.op_numpy(matrix.to_array(), scalar)
Expand All @@ -385,6 +395,7 @@ class BinaryOpMixin(_GenericOpMixin):
Mix-in for binary mathematical operations on Data instances (e.g. binary
addition).
"""

def test_mathematically_correct(self, op, data_l, data_r, out_type):
"""
Test that the binary operation is mathematically correct for all the
Expand Down Expand Up @@ -414,17 +425,14 @@ class TernaryOpMixin(_GenericOpMixin):
Mix-in for ternary mathematical operations on Data instances (e.g. inner
product with an operator in the middle). This is pretty rare.
"""
def test_mathematically_correct(self, op,
data_l, data_m, data_r,
out_type):

def test_mathematically_correct(self, op, data_l, data_m, data_r, out_type):
"""
Test that the binary operation is mathematically correct for all the
known type specialisations.
"""
left, mid, right = data_l(), data_m(), data_r()
expected = self.op_numpy(left.to_array(),
mid.to_array(),
right.to_array())
expected = self.op_numpy(left.to_array(), mid.to_array(), right.to_array())
test = op(left, mid, right)
assert isinstance(test, out_type)
if issubclass(out_type, Data):
Expand All @@ -444,6 +452,7 @@ def test_incorrect_shape_raises(self, op, data_l, data_m, data_r):

# And now finally we get into the meat of the actual mathematical tests.


class TestAdd(BinaryOpMixin):
def op_numpy(self, left, right, scale):
return np.add(left, scale * right)
Expand All @@ -457,8 +466,9 @@ def op_numpy(self, left, right, scale):
# `add` has an additional scalar parameter, because the operation is
# actually more like `A + c*B`. We just parametrise that scalar
# separately.
@pytest.mark.parametrize('scale', [None, 0.2, 0.5j],
ids=['unscaled', 'scale[real]', 'scale[complex]'])
@pytest.mark.parametrize(
"scale", [None, 0.2, 0.5j], ids=["unscaled", "scale[real]", "scale[complex]"]
)
def test_mathematically_correct(self, op, data_l, data_r, out_type, scale):
"""
Test that the binary operation is mathematically correct for
Expand Down
6 changes: 3 additions & 3 deletions tests/test_linear_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def matrix(size, density):

def change_dtype(A, dtype):
"""Changes a numpy matrix to tensorflow, scipy sparse or to a qutip.Data
specified by dtype"""
specified by dtype."""
if dtype == np:
return A
elif dtype == tf:
Expand All @@ -64,8 +64,8 @@ def change_dtype(A, dtype):

#Supported dtypes
dtype_list = [np, tf, sc, qt.data.Dense, qt.data.CSR, qtf.data.DenseTensor]
dtype_ids = ['numpy', 'tensorflow', 'scipy(CSR)', 'qutip(Dense)', 'qutip(CSR)',
'qutip(DenseTensor)']
dtype_ids = ['numpy', 'tensorflow', 'scipy_CSR', 'qutip_Dense', 'qutip_CSR',
'qutip_DenseTensor']
@pytest.fixture(params = dtype_list, ids=dtype_ids)
def dtype(request): return request.param

Expand Down

0 comments on commit a361661

Please sign in to comment.