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

Align sptensor comparison operator functionality #287

Open
dmdunla opened this issue Nov 2, 2023 · 1 comment
Open

Align sptensor comparison operator functionality #287

dmdunla opened this issue Nov 2, 2023 · 1 comment
Labels
bug Something isn't working
Milestone

Comments

@dmdunla
Copy link
Collaborator

dmdunla commented Nov 2, 2023

Currently, there is a TODO listed in the sptensor class, noting that the comparison operators (__gt__, __ge__, __lt__, __le__, __eq__, __ne__) share a lot of code and could be streamlined.

When evaluating sptensor related to #286, I also saw that comparisons involving empty tensors led to errors that were not useful. The operator __lt__ works for empty sptensor objects, but the others do not. This should be fixed.

Suggestion: Provide support for all comparison operators to handle empty tensors. When either or both operands are empty tensors (sparse or dense), the results should be an empty tensor of the type of the first operand (i.e., self).

Reproducing the current functionality:

>>> import pyttb as ttb
>>> import numpy as np

>>> S = ttb.sptensor(shape=(2,2))

>>> S < S
empty sparse tensor of shape (2, 2)

>>> S > S
empty sparse tensor of shape (2, 2)

>>> S == S
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/dmdunla/dev/github.com/pyttb/pyttb/sptensor.py", line 2657, in __eq__
    (self.vals[nzsubsIdx] == other.vals[iother]).transpose()[0], :
IndexError: index 0 is out of bounds for axis 0 with size 0

>>> S != S
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/dmdunla/dev/github.com/pyttb/pyttb/sptensor.py", line 2751, in __ne__
    self.extract(self.subs[subs2]) != other.extract(self.subs[subs2])
  File "/Users/dmdunla/dev/github.com/pyttb/pyttb/sptensor.py", line 717, in extract
    invalid = (searchsubs < 0) | (searchsubs >= np.array(self.shape))
ValueError: operands could not be broadcast together with shapes (0,0) (2,) 

>>> S <= S
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/dmdunla/dev/github.com/pyttb/pyttb/sptensor.py", line 3070, in __le__
    subs = np.vstack((subs1, subs2, subs3, subs4))
  File "/Users/dmdunla/opt/anaconda3/lib/python3.9/site-packages/numpy/core/shape_base.py", line 289, in vstack
    return _nx.concatenate(arrs, 0, dtype=dtype, casting=casting)
ValueError: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 0 and the array at index 3 has size 2

>>> S >= S
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/dmdunla/dev/github.com/pyttb/pyttb/sptensor.py", line 3235, in __ge__
    return other.__le__(self)
  File "/Users/dmdunla/dev/github.com/pyttb/pyttb/sptensor.py", line 3070, in __le__
    subs = np.vstack((subs1, subs2, subs3, subs4))
  File "/Users/dmdunla/opt/anaconda3/lib/python3.9/site-packages/numpy/core/shape_base.py", line 289, in vstack
    return _nx.concatenate(arrs, 0, dtype=dtype, casting=casting)
ValueError: all the input array dimensions except for the concatenation axis must match exactly, but along dimension 1, the array at index 0 has size 0 and the array at index 3 has size 2

>>> S < S.to_tensor()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/dmdunla/dev/github.com/pyttb/pyttb/sptensor.py", line 3175, in __lt__
    subs2 = self.subs[self.vals.transpose()[0] < other[self.subs], :]
IndexError: index 0 is out of bounds for axis 0 with size 0

>>> S.to_tensor() < S
tensor of shape (2, 2)
data[:, :] =
[[False False]
 [False False]]

>>> T = ttb.tensor()
>>> T
empty tensor of shape ()
data = []

>>> T < T
empty tensor of shape (0,)
data = []
@dmdunla dmdunla added the bug Something isn't working label Nov 2, 2023
@dmdunla dmdunla added this to the v2.0 milestone Nov 2, 2023
@jeremy-myers
Copy link
Collaborator

There are some curiosities with empty sptensor objects that may help explain the bug. Consider these two empty sptensors:

>>> S = ttb.sptensor(shape=(2,2)) # taken from Danny's example
>>> T = ttb.sptensor(np.array([]), np.array([]), shape=(4,4,4)) # adapted from test_sptensor.py, test_sptensor__lt__()
>>> S < S
empty sparse tensor of shape (2, 2) # as seen in Danny's example
>>> T < T
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jermyer/dev/pyttb/pyttb/sptensor.py", line 2967, in __lt__
    subs1 = np.empty(shape=(0, other.subs.shape[1]))
                               ~~~~~~~~~~~~~~~~^^^
IndexError: tuple index out of range

Zooming in more closely:

>>> S.subs
array([], shape=(1, 0), dtype=int64)
>>> S.vals
array([], shape=(1, 0), dtype=float64)
>>> T.subs
array([], dtype=float64)
>>> T.vals
array([], dtype=float64)

Provided a shape, S.subs and S.vals have a shape (even though it isn't consistent with the shape of S. Not provided a shape, T.subs and T.vals don't have a shape (which I think is expected). However, __lt__() fails here since the returned empty container has a shape parameter that depends on the shape of the subs, which in the case of T is a 1D array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants