Skip to content

Fixing issues with np.reshape; requires order='F' to align with Matla… #39

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

Merged
merged 1 commit into from
Jul 13, 2022
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
29 changes: 11 additions & 18 deletions pyttb/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,10 +335,8 @@ def innerprod(self, other):
if isinstance(other, ttb.tensor):
if self.shape != other.shape:
assert False, 'Inner product must be between tensors of the same size'
x = np.reshape(self.data, (self.data.size,))
y = np.reshape(other.data, (other.data.size,))
#x = np.reshape(self.data, (1, self.data.size))
#y = np.reshape(other.data, (other.data.size, 1))
x = np.reshape(self.data, (self.data.size,), order='F')
y = np.reshape(other.data, (other.data.size,), order='F')
return x.dot(y)
elif isinstance(other, (ttb.ktensor, ttb.sptensor, ttb.ttensor)):
# Reverse arguments and call specializer code
Expand Down Expand Up @@ -770,20 +768,16 @@ def squeeze(self):
def symmetrize(self, grps=None, version=None):
"""
Symmetrize a tensor in the specified modes

Notes
-----
It is *the same or less* work to just call X = symmetrize(X) then to first check if X is symmetric and then
symmetrize it, even if X is already symmetric.

Parameters
----------
grps
version

Returns
-------

"""
n = self.ndims
sz = np.array(self.shape)
Expand Down Expand Up @@ -962,7 +956,7 @@ def ttsv(self, vector, dims=None, version = None):
elif dims > 0:
assert False, "Invalid modes in ttsv"

if version is not None: # Calculate the old way
if version == 1: # Calculate the old way
P = self.ndims
X = np.array([vector for i in range(P)])
if dims == 0:
Expand All @@ -972,9 +966,7 @@ def ttsv(self, vector, dims=None, version = None):
else:
return self.ttv(X, -np.arange(1, -dims+1))

else: # Calculate the new way
if dims != 0:
assert False, "New version only support vector times all modes"
elif version == 2 or version is None: # Calculate the new way
d = self.ndims
sz = self.shape[0] # Sizes of all modes must be the same

Expand All @@ -983,17 +975,18 @@ def ttsv(self, vector, dims=None, version = None):

y = self.data
for i in range(drem, 0, -1):
yy = np.reshape(y, (sz**(dnew + i -1), sz))
yy = np.reshape(y, (sz**(dnew + i -1), sz), order='F')
y = yy.dot(vector)

# Convert to matrix if 2-way or convert back to tensor if result is >= 3-way
# TODO: currently this only support scalar return so these are ignored in coverage
if dnew == 2: # pragma: no cover
return np.reshape(y, [sz, sz])
elif dnew > 2: # pragma: no cover
return ttb.tensor.from_data(np.reshape(y, sz*np.ones(dnew)))
if dnew == 2:
return np.reshape(y, [sz, sz], order='F')
elif dnew > 2:
return ttb.tensor.from_data(np.reshape(y, sz*np.ones(dnew, dtype=np.int), order='F'))
else:
return y
else:
assert False, "Invalid value for version; should be None, 1, or 2"

def __setitem__(self, key, value):
"""
Expand Down
85 changes: 69 additions & 16 deletions tests/test_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ def test_tensor_squeeze(sample_tensor_2way):
def test_tensor_ttv(sample_tensor_2way, sample_tensor_3way, sample_tensor_4way):
(params2, tensorInstance2) = sample_tensor_2way
(params3, tensorInstance3) = sample_tensor_3way
(params3, tensorInstance4) = sample_tensor_4way
(params4, tensorInstance4) = sample_tensor_4way

# Wrong shape vector
with pytest.raises(AssertionError) as excinfo:
Expand Down Expand Up @@ -1028,31 +1028,64 @@ def test_tensor_ttv(sample_tensor_2way, sample_tensor_3way, sample_tensor_4way):
assert tensorInstance4.ttv(np.array([np.array([1, 1, 1]), np.array([1, 1, 1]), np.array([1, 1, 1]), np.array([1, 1, 1])])) == 3321

@pytest.mark.indevelopment
def test_tensor_ttsv(sample_tensor_2way):
(params, tensorInstance) = sample_tensor_2way
tensorInstance = ttb.tensor.from_data(np.ones((4, 4, 4)))
vector = np.array([1, 1, 1, 1])
def test_tensor_ttsv(sample_tensor_4way):

# 3-way
tensorInstance3 = ttb.tensor.from_data(np.ones((4, 4, 4)))
vector3 = np.array([4, 3, 2, 1])
assert tensorInstance3.ttsv(vector3, version=1) == 1000
assert (tensorInstance3.ttsv(vector3, dims=-1, version=1) == 100 * np.ones((4,))).all()
assert (tensorInstance3.ttsv(vector3, dims=-2, version=1) == 10 * np.ones((4,4))).all()

# Invalid dims
with pytest.raises(AssertionError) as excinfo:
tensorInstance.ttsv(vector, dims = 1)
tensorInstance3.ttsv(vector3, dims = 1)
assert "Invalid modes in ttsv" in str(excinfo)

# 4-way tensor
(params4, tensorInstance4) = sample_tensor_4way
T4ttsv = tensorInstance4.ttsv(np.array([1,2,3]), -3, version=1)
data4_3 = np.array([[[222, 276, 330],
[240, 294, 348],
[258, 312, 366]],
[[228, 282, 336],
[246, 300, 354],
[264, 318, 372]],
[[234, 288, 342],
[252, 306, 360],
[270, 324, 378]]])
assert (T4ttsv.data == data4_3).all()

assert tensorInstance.ttsv(vector, version=1) == 64
assert (tensorInstance.ttsv(vector, dims=-1, version=1) == np.array([16, 16, 16, 16])).all()
# 5-way dense tensor
shape = (3,3,3,3,3)
T5 = ttb.tensor.from_data(np.arange(1,np.prod(shape)+1), shape)
T5ttsv = T5.ttsv(np.array([1,2,3]), -3, version=1)
data5_3 = np.array([[[5220, 5544, 5868],
[5328, 5652, 5976],
[5436, 5760, 6084]],
[[5256, 5580, 5904],
[5364, 5688, 6012],
[5472, 5796, 6120]],
[[5292, 5616, 5940],
[5400, 5724, 6048],
[5508, 5832, 6156]]])
assert (T5ttsv.data == data5_3).all()

# Test new algorithm, version=2

tensorInstance = ttb.tensor.from_data(np.ones((4, 4, 4, 4)))
assert tensorInstance.ttsv(vector, dims=-3, version=1).isequal(tensorInstance.ttv(vector, 0))
# 3-way
assert tensorInstance3.ttsv(vector3) == 1000
assert (tensorInstance3.ttsv(vector3, dims=-1) == 100 * np.ones((4,))).all()
assert (tensorInstance3.ttsv(vector3, dims=-2) == 10 * np.ones((4,4))).all()

# Test new algorithm
# 4-way tensor
T4ttsv2 = tensorInstance4.ttsv(np.array([1,2,3]), -3)
assert (T4ttsv2.data == data4_3).all()

# Only works for all modes of equal length
# Incorrect version requested
with pytest.raises(AssertionError) as excinfo:
tensorInstance.ttsv(vector, dims=-1)
assert "New version only support vector times all modes" in str(excinfo)
tensorInstance = ttb.tensor.from_data(np.ones((4, 4, 4)))
assert tensorInstance.ttsv(vector) == 64
tensorInstance4.ttsv(np.array([1,2,3]), -3, version=3)
assert "Invalid value for version; should be None, 1, or 2" in str(excinfo)

@pytest.mark.indevelopment
def test_tensor_issymmetric(sample_tensor_2way):
Expand Down Expand Up @@ -1083,19 +1116,39 @@ def test_tensor_symmetrize(sample_tensor_2way):

# Test new default version

# 2-way
symmetricData = np.array([[[0.5, 0, 0.5, 0], [0, 0, 0, 0], [0.5, 0, 0, 0], [0, 0, 0, 0]],
[[0, 0, 0, 0], [0, 2.5, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
[[0.5, 0, 0, 0], [0, 0, 0, 0], [0, 0, 3.5, 0], [0, 0, 0, 0]],
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]])
symmetricTensor = ttb.tensor.from_data(symmetricData)
assert symmetricTensor.symmetrize().isequal(symmetricTensor)

# 3-way
symmetricData = np.zeros((4, 4, 4))
symmetricData[1, 2, 1] = 1
symmetricTensor = ttb.tensor.from_data(symmetricData)
print(f'\nsymmetricTensor:\n{symmetricTensor}')
assert symmetricTensor.issymmetric() is False
print(f'\nsymmetricTensor.symmetrize():\n{symmetricTensor.symmetrize()}')
assert (symmetricTensor.symmetrize()).issymmetric()

# 3-way
shape = (2,2,2)
T3 = ttb.tensor.from_data(np.arange(1,np.prod(shape)+1), shape)
T3sym = T3.symmetrize()
print(f'\nT3sym:')
print(T3sym)
data3 = np.array([[[1, 3+1/3],
[3+1/3, 5+2/3]],
[[3+1/3, 5+2/3],
[5+2/3, 8 ]]])
assert (T3sym.data == data3).all()

#T3syms_2_1_3 = T3.symmetrize(grps=[[1], [0,2]])
#print(f'\nT3syms_2_1_3:')
#print(T3syms_2_1_3)

with pytest.raises(AssertionError) as excinfo:
symmetricTensor.symmetrize(grps=np.array([[0, 1], [1, 2]]))
assert "Cannot have overlapping symmetries" in str(excinfo)
Expand Down