Skip to content

Implement missing ufunc.outer functions for binary ops #55

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

Closed
ricardoV94 opened this issue Nov 29, 2022 · 3 comments · Fixed by #745
Closed

Implement missing ufunc.outer functions for binary ops #55

ricardoV94 opened this issue Nov 29, 2022 · 3 comments · Fixed by #745

Comments

@ricardoV94
Copy link
Member

Please describe the purpose of filing this issue

https://numpy.org/doc/stable/reference/generated/numpy.ufunc.outer.html

@ricardoV94
Copy link
Member Author

ricardoV94 commented Nov 29, 2022

Can we obtain this with simple dimshuffles?

import numpy as np

x = np.arange(3)
y = np.arange(5)

np.add.outer(x, y)
# array([[0, 1, 2, 3, 4],
#        [1, 2, 3, 4, 5],
#        [2, 3, 4, 5, 6]])

x[:, None] + y[None, :] 
# array([[0, 1, 2, 3, 4],
#        [1, 2, 3, 4, 5],
#        [2, 3, 4, 5, 6]])
def outer(op, x, y):
  x_ = np.expand_dims(x, tuple(range(-y.ndim, 0)))
  y_ = np.expand_dims(y, tuple(range(x.ndim)))
  return op(x_, y_)

x = np.ones((3, 5))
y = np.ones((7, 1, 3))

np.testing.assert_array_equal(
  outer(np.add, x, y),
  np.add.outer(x, y)
)

@ricardoV94 ricardoV94 changed the title Implement missing ufunc.outer functions for binary ops Implement missing ufunc.outer functions for binary ops Nov 29, 2022
@ricardoV94
Copy link
Member Author

ricardoV94 commented Nov 29, 2022

Would require adding an outer method to the binary Elemwise Ops that would implement the same PyTensor logic as the NumPy outer function above.

@ricardoV94
Copy link
Member Author

ricardoV94 commented Mar 22, 2023

The idea would be to add an outer method to Elemwise

class Elemwise(OpenMPOp):

Roughly, it might look something like:

def outer(self, x, y):
  # Add and Mul scalar op nin is -1, because they allow arbitrary number of inputs
  if self.scalar_op.nin not in (-1, 2):
    raise NotImplementedError("outer is only available for binary operators")
  
  x_ = pt.expand_dims(x, tuple(range(-y.ndim, 0)))
  y_ = pt.expand_dims(y, tuple(range(x.ndim)))
  return self(x_, y_)

And then one should be able to call:

import pytensor.tensor as pt
z = pt.add.outer(x, y)

CC @Raj-Parekh24, let me know if this is sufficient to get you started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant