Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test coverage #52

Merged
merged 7 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions pyttb/cp_apr.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def cp_apr(tensor, rank, algorithm='mu', stoptol=1e-4, stoptime=1e6, maxiters=10
kappa=0.01, kappatol=1e-10, epsActive=1e-8, mu0=1e-5, precompinds=True, inexact=True,
lbfgsMem=3):
"""
Compute nonnegative CP with alternating Poisson regression.
Compute non-negative CP with alternating Poisson regression.

Parameters
----------
Expand Down Expand Up @@ -376,9 +376,10 @@ def tt_cp_apr_pdnr(tensor, rank, init, stoptol, stoptime, maxiters, maxinneriter
sparseIx = []
for n in range(N):
num_rows = M[n].shape[0]
sparseIx.append(np.zeros((num_rows, 1)))
row_indices = []
for jj in range(num_rows):
sparseIx[n][jj] = np.where(tensor.subs[:, n] == jj)[0]
row_indices.append(np.where(tensor.subs[:, n] == jj)[0])
sparseIx.append(row_indices)

if printitn > 0:
print('done\n')
Expand Down Expand Up @@ -426,7 +427,7 @@ def tt_cp_apr_pdnr(tensor, rank, init, stoptol, stoptime, maxiters, maxinneriter
M.factor_matrices[n][jj, :] = 0
continue

x_row = tensor.vals(sparse_indices)
x_row = tensor.vals[sparse_indices]

# Calculate just the columns of Pi needed for this row.
Pi = ttb.tt_calcpi_prowsubprob(tensor, M, rank, n, N, isSparse, sparse_indices)
Expand Down Expand Up @@ -683,9 +684,10 @@ def tt_cp_apr_pqnr(tensor, rank, init, stoptol, stoptime, maxiters, maxinneriter
sparseIx = []
for n in range(N):
num_rows = M[n].shape[0]
sparseIx.append(np.zeros((num_rows, 1)))
row_indices = []
for jj in range(num_rows):
sparseIx[n][jj] = np.where(tensor.subs[:, n] == jj)[0]
row_indices.append(np.where(tensor.subs[:, n] == jj)[0])
sparseIx.append(row_indices)

if printitn > 0:
print('done\n')
Expand Down Expand Up @@ -726,7 +728,7 @@ def tt_cp_apr_pqnr(tensor, rank, init, stoptol, stoptime, maxiters, maxinneriter
M.factor_matrices[n][jj, :] = 0
continue

x_row = tensor.vals(sparse_indices)
x_row = tensor.vals[sparse_indices]

# Calculate just the columns of Pi needed for this row.
Pi = ttb.tt_calcpi_prowsubprob(tensor, M, rank, n, N, isSparse, sparse_indices)
Expand Down Expand Up @@ -1227,7 +1229,7 @@ def tt_loglikelihood_row(isSparse, data_row, model_row, Pi):
"""
term1 = -np.sum(model_row)
if isSparse:
term2 = np.sum(data_row.transpose() * np.log(model_row.dot(Pi)))
term2 = np.sum(data_row.transpose() * np.log(model_row.dot(Pi.transpose())))
else:
b_pi = model_row.dot(Pi.transpose())
term2 = 0
Expand Down
18 changes: 10 additions & 8 deletions pyttb/sptensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,6 @@ def end(self, k = None):
Parameters
----------
k: int Dimension for subscript indexing
n: int 1 for linear indexing, ndims for subscript

Returns
-------
Expand Down Expand Up @@ -419,11 +418,14 @@ def extract(self, searchsubs):
invalid = (searchsubs < 0) | (searchsubs >= np.array(self.shape))
badloc = np.where(np.sum(invalid, axis=1) > 0)
if badloc[0].size > 0:
print('The following subscripts are invalid: \n')
error_msg = "The following subscripts are invalid: \n"
badsubs = searchsubs[badloc, :]
for i in np.arange(0, badloc[0].size):
print('\tsubscript = {}) \n'.format(tt_intvec2str(badsubs[i, :])))
assert False, 'Invalid subscripts'
error_msg += f"\tsubscript = {tt_intvec2str(badsubs[i, :])} \n"
assert False, (
f"{error_msg}"
'Invalid subscripts'
)

# Set the default answer to zero
a = np.zeros(shape=(p, 1), dtype=self.vals.dtype)
Expand Down Expand Up @@ -687,7 +689,7 @@ def mttkrp(self, U, n):
>>> subs = np.array([[1, 1, 1], [1, 1, 3], [2, 2, 2], [3, 3, 3]])
>>> vals = np.array([[0.5], [1.5], [2.5], [3.5]])
>>> shape = (4, 4, 4)
>>> sptensorInstance.from_data(subs, vals, shape)
>>> sptensorInstance = sptensor.from_data(subs, vals, shape)
>>> sptensorInstance.mttkrp(np.array([matrix, matrix, matrix]), 0)
[[0, 0, 0, 0],
[2, 2, 2, 2],
Expand Down Expand Up @@ -962,7 +964,7 @@ def subdims(self, region):
--------
>>> subs = np.array([[1, 1, 1], [1, 1, 3], [2, 2, 2], [3, 3, 3]])
>>> vals = np.array([[0.5], [1.5], [2.5], [3.5]])
>>> shape = np.array([4, 4, 4])
>>> shape = (4, 4, 4)
>>> sp = sptensor.from_data(subs,vals,shape)
>>> region = np.ndarray([1, [1], [1,3]])
>>> loc = sp.subdims(region)
Expand Down Expand Up @@ -1706,7 +1708,7 @@ def __mul__(self, other):

Parameters
----------
:class:`pyttb.sptensor`, :class:`pyttb.tensor`, float, int
other: :class:`pyttb.sptensor`, :class:`pyttb.tensor`, float, int

Returns
-------
Expand Down Expand Up @@ -1748,7 +1750,7 @@ def __rmul__(self, other):

Parameters
----------
float, int
other: float, int

Returns
-------
Expand Down
7 changes: 2 additions & 5 deletions pyttb/tenmat.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class tenmat(object):

"""

def __init__(self, *args):
def __init__(self):
"""
TENSOR Create empty tensor.
"""
Expand Down Expand Up @@ -190,10 +190,7 @@ def ndims(self):
-------
int
"""
if self.shape == (0,):
return 0
else:
return len(self.shape)
return len(self.shape)

def norm(self):
"""
Expand Down
5 changes: 2 additions & 3 deletions pyttb/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ def from_tensor_type(cls, source):
order = np.hstack([source.rindices, source.cindices])
data = np.reshape(source.data.copy(), np.array(shape)[order], order='F')
if order.size > 1:
# data = ipermute(data, order)
data = np.transpose(data, np.argsort(order))
return cls.from_data(data, shape)

Expand Down Expand Up @@ -944,7 +943,7 @@ def ttm(self, matrix, dims=None, transpose=False):

def ttt(self, other, selfdims=None, otherdims=None):
"""
Tensor mulitplication (tensor times tensor)
Tensor multiplication (tensor times tensor)

Parameters
----------
Expand Down Expand Up @@ -1455,7 +1454,7 @@ def tensor_add(x, y):

def __radd__(self, other):
"""
Reverse binary addition (+) for tensors
Right binary addition (+) for tensors

Parameters
----------
Expand Down
59 changes: 59 additions & 0 deletions tests/test_cp_apr.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ def test_cpapr_mu(capsys):
capsys.readouterr()
assert output['nTotalIters'] == 2

# Edge cases
# Confirm timeout works
non_correct_answer = ktensorInstance*2
_ = ttb.cp_apr(tensorInstance, 2, init=non_correct_answer, stoptime=-1)
out, _ = capsys.readouterr()
assert "time limit exceeded" in out

@pytest.mark.indevelopment
def test_cpapr_pdnr(capsys):
# Test simple case
Expand All @@ -139,6 +146,23 @@ def test_cpapr_pdnr(capsys):
capsys.readouterr()
assert np.isclose(M.full().data, ktensorInstance.full().data, rtol=1e-04).all()

# Try solve with sptensor
sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance)
np.random.seed(123)
M, _, _ = ttb.cp_apr(sptensorInstance, 2, algorithm="pdnr")
capsys.readouterr()
assert np.isclose(M.full().data, ktensorInstance.full().data, rtol=1e-04).all()
M, _, _ = ttb.cp_apr(sptensorInstance, 2, algorithm="pdnr", precompinds=False)
capsys.readouterr()
assert np.isclose(M.full().data, ktensorInstance.full().data, rtol=1e-04).all()

# Edge cases
# Confirm timeout works
non_correct_answer = ktensorInstance*2
_ = ttb.cp_apr(tensorInstance, 2, init=non_correct_answer, algorithm="pdnr", stoptime=-1)
out, _ = capsys.readouterr()
assert "time limit exceeded" in out

@pytest.mark.indevelopment
def test_cpapr_pqnr(capsys):
# Test simple case
Expand All @@ -165,6 +189,22 @@ def test_cpapr_pqnr(capsys):
capsys.readouterr()
assert np.isclose(M.full().data, ktensorInstance.full().data, rtol=1e-01).all()

# Try solve with sptensor
sptensorInstance = ttb.sptensor.from_tensor_type(tensorInstance)
np.random.seed(123)
M, _, _ = ttb.cp_apr(sptensorInstance, 2, algorithm="pqnr")
capsys.readouterr()
assert np.isclose(M.full().data, ktensorInstance.full().data, rtol=1e-01).all()
M, _, _ = ttb.cp_apr(sptensorInstance, 2, algorithm="pqnr", precompinds=False)
capsys.readouterr()
assert np.isclose(M.full().data, ktensorInstance.full().data, rtol=1e-01).all()

# Edge cases
# Confirm timeout works
_ = ttb.cp_apr(tensorInstance, 2, algorithm="pqnr", stoptime=-1)
out, _ = capsys.readouterr()
assert "time limit exceeded" in out


# PDNR tests below
@pytest.mark.indevelopment
Expand Down Expand Up @@ -367,3 +407,22 @@ def test_getSearchDirPqnr():
search, pred = ttb.getSearchDirPqnr(model_row, phi, 1e-6, delta_model, delta_grad, phi, 1, 5, False)
# This only verifies that for the right shaped input nothing crashes. Doesn't verify correctness
assert True

def test_cp_apr_negative_tests():
dense_tensor = ttb.tensor.from_data(np.ones((2, 2, 2)))
bad_weights = np.array([8.])
bad_factors = [np.array([[1.]])] * 3
bad_initial_guess_shape = ttb.ktensor.from_data(bad_weights, bad_factors)
with pytest.raises(AssertionError):
ttb.cp_apr(dense_tensor, init=bad_initial_guess_shape, rank=1)
good_weights = np.array([8.] * 3)
good_factor = np.array([[1., 1., 1.], [1., 1., 1.]])
bad_initial_guess_factors = ttb.ktensor.from_data(good_weights, [-1. * good_factor] * 3)
with pytest.raises(AssertionError):
ttb.cp_apr(dense_tensor, init=bad_initial_guess_factors, rank=3)
bad_initial_guess_weight = ttb.ktensor.from_data(-1. * good_weights, [good_factor] * 3)
with pytest.raises(AssertionError):
ttb.cp_apr(dense_tensor, init=bad_initial_guess_weight, rank=3)

with pytest.raises(AssertionError):
ttb.cp_apr(dense_tensor, rank=1, algorithm='UNSUPPORTED_ALG')
10 changes: 10 additions & 0 deletions tests/test_packaging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import pyttb as ttb
import pytest


def test_package_smoke():
"""A few sanity checks to make sure things don't explode"""
assert len(ttb.__version__) > 0
# Make sure warnings filter doesn't crash
ttb.ignore_warnings(False)
ttb.ignore_warnings(True)
16 changes: 12 additions & 4 deletions tests/test_pyttb_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# U.S. Government retains certain rights in this software.

import pyttb as ttb
import logging
import numpy as np
import pytest
import scipy.sparse as sparse
Expand Down Expand Up @@ -172,8 +173,6 @@ def tensor_max(x):
assert "Tensor 2 is not the same size as the first tensor input" in str(excinfo)




@pytest.mark.indevelopment
def test_tt_setdiff_rows():
a = np.array([[4, 6], [1, 9], [2, 6], [2, 6], [99, 0]])
Expand All @@ -184,13 +183,22 @@ def test_tt_setdiff_rows():
b = np.array([[1, 7], [1, 8]])
assert (ttb.tt_setdiff_rows(a, b) == np.array([0, 1])).all()

a = np.array([[4, 6], [1, 9]])
b = np.array([])
assert (ttb.tt_setdiff_rows(a, b) == np.arange(a.shape[0])).all()
assert (ttb.tt_setdiff_rows(b, a) == b).all()

@pytest.mark.indevelopment
def test_tt_intersect_rows():
a = np.array([[4, 6], [1, 9], [2, 6], [2, 6]])
b = np.array([[1, 7], [1, 8], [2, 6], [2, 1], [2, 4], [4, 6], [4, 7], [5, 9], [5, 2], [5, 1]])
assert (ttb.tt_intersect_rows(a, b) == np.array([2, 0])).all()

a = np.array([[4, 6], [1, 9]])
b = np.array([])
assert (ttb.tt_intersect_rows(a, b) == b).all()
assert (ttb.tt_intersect_rows(a, b) == ttb.tt_intersect_rows(b, a)).all()


@pytest.mark.indevelopment
def test_tt_ismember_rows():
Expand Down Expand Up @@ -330,13 +338,13 @@ def test_tt_ind2sub_valid():
subs = np.array([[0, 0, 0], [1, 1, 1], [3, 3, 3]])
idx = np.array([0, 21, 63])
shape = (4, 4, 4)
print(f'\nttb.tt_ind2sub(shape, idx): {ttb.tt_ind2sub(shape, idx)}')
logging.debug(f'\nttb.tt_ind2sub(shape, idx): {ttb.tt_ind2sub(shape, idx)}')
assert (ttb.tt_ind2sub(shape, idx) == subs).all()

subs = np.array([[1, 0], [0, 1]])
idx = np.array([1, 2])
shape = (2, 2)
print(f'\nttb.tt_ind2sub(shape, idx): {ttb.tt_ind2sub(shape, idx)}')
logging.debug(f'\nttb.tt_ind2sub(shape, idx): {ttb.tt_ind2sub(shape, idx)}')
assert (ttb.tt_ind2sub(shape, idx) == subs).all()

empty = np.array([])
Expand Down
32 changes: 20 additions & 12 deletions tests/test_sptensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# U.S. Government retains certain rights in this software.

import pyttb as ttb
import logging
import numpy as np
import pytest
import scipy.sparse as sparse
Expand Down Expand Up @@ -47,9 +48,9 @@ def test_sptensor_initialization_from_tensor_type(sample_sptensor):
inputData = np.array([[1, 2, 3], [4, 5, 6]])
tensorInstance = ttb.tensor.from_data(inputData)
sptensorFromTensor = ttb.sptensor.from_tensor_type(tensorInstance)
print(f'inputData = {inputData}')
print(f'tensorInstance = {tensorInstance}')
print(f'sptensorFromTensor = {sptensorFromTensor}')
logging.debug(f'inputData = {inputData}')
logging.debug(f'tensorInstance = {tensorInstance}')
logging.debug(f'sptensorFromTensor = {sptensorFromTensor}')
assert (sptensorFromTensor.subs == ttb.tt_ind2sub(inputData.shape, np.arange(0, inputData.size))).all()
assert (sptensorFromTensor.vals == inputData.reshape((inputData.size, 1), order='F')).all()
assert (sptensorFromTensor.shape == inputData.shape)
Expand All @@ -59,6 +60,11 @@ def test_sptensor_initialization_from_tensor_type(sample_sptensor):
sptensorFromCOOMatrix = ttb.sptensor.from_tensor_type(sparse.coo_matrix(inputData))
assert (sptensorFromCOOMatrix.spmatrix() != sparse.coo_matrix(inputData)).nnz == 0

# Negative Tests
with pytest.raises(AssertionError):
invalid_tensor_type = []
ttb.sptensor.from_tensor_type(invalid_tensor_type)

@pytest.mark.indevelopment
def test_sptensor_initialization_from_function():

Expand Down Expand Up @@ -247,6 +253,8 @@ def test_sptensor__getitem__(sample_sptensor):
## Case 2 Linear Indexing
ind = ttb.tt_sub2ind(data['shape'], np.array([[1, 1, 1], [1, 1, 3], [2, 2, 2]]))
assert (sptensorInstance[ind] == np.array([[0.5], [1.5], [2.5]])).all()
list_ind = list(ind)
assert (sptensorInstance[list_ind] == np.array([[0.5], [1.5], [2.5]])).all()
ind2 = ttb.tt_sub2ind(data['shape'], np.array([[1, 1, 1], [1, 1, 3]]))
assert (sptensorInstance[ind2] == np.array([[0.5], [1.5]])).all()
with pytest.raises(AssertionError) as excinfo:
Expand Down Expand Up @@ -622,15 +630,15 @@ def test_sptensor__eq__(sample_sptensor):

denseTensor = ttb.tensor.from_tensor_type(sptensorInstance)
eqSptensor = sptensorInstance == denseTensor
print(f"\ndenseTensor = {denseTensor}")
print(f"\nsptensorInstance = {sptensorInstance}")
print(f"\ntype(eqSptensor.subs) = \n{type(eqSptensor.subs)}")
logging.debug(f"\ndenseTensor = {denseTensor}")
logging.debug(f"\nsptensorInstance = {sptensorInstance}")
logging.debug(f"\ntype(eqSptensor.subs) = \n{type(eqSptensor.subs)}")
for i in range(eqSptensor.subs.shape[0]):
print(f"{i}\t{eqSptensor.subs[i,:]}")
print(f"\neqSptensor.subs = \n{eqSptensor.subs}")
print(f"\neqSptensor.subs.shape[0] = {eqSptensor.subs.shape[0]}")
print(f"\nsptensorInstance.shape = {sptensorInstance.shape}")
print(f"\nnp.prod(sptensorInstance.shape) = {np.prod(sptensorInstance.shape)}")
logging.debug(f"{i}\t{eqSptensor.subs[i,:]}")
logging.debug(f"\neqSptensor.subs = \n{eqSptensor.subs}")
logging.debug(f"\neqSptensor.subs.shape[0] = {eqSptensor.subs.shape[0]}")
logging.debug(f"\nsptensorInstance.shape = {sptensorInstance.shape}")
logging.debug(f"\nnp.prod(sptensorInstance.shape) = {np.prod(sptensorInstance.shape)}")
assert eqSptensor.subs.shape[0] == np.prod(sptensorInstance.shape)

denseTensor = ttb.tensor.from_data(np.ones((5, 5, 5)))
Expand Down Expand Up @@ -1371,7 +1379,7 @@ def test_sptensor_ttm(sample_sptensor):
# This is a multiway multiplication yielding a sparse tensor, yielding a dense tensor relies on tensor.ttm
matrix = sparse.coo_matrix(np.eye(4))
list_of_matrices = [matrix, matrix, matrix]
assert sptensorInstance.ttm(list_of_matrices, dims=np.array([0, 1, 2])).isequal(sptensorInstance)
assert sptensorInstance.ttm(list_of_matrices, dims=[0, 1, 2]).isequal(sptensorInstance)

with pytest.raises(AssertionError) as excinfo:
sptensorInstance.ttm(sparse.coo_matrix(np.ones((5, 5))), dims=0)
Expand Down
Loading