-
Notifications
You must be signed in to change notification settings - Fork 20
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
refactor Gaussian to use Tensor api instead of torch.Tensor #296
Conversation
Instead of adding methods, let's add ops, following e.g. # in ops.py
class TriangularSolveOp(Op):
pass
@TriangularSolveOp
def triangular_solve(x, y):
raise NotImplementedError
# in torch.py
@eager.register(Binary, TriangularSolveOp, Tensor, Tensor)
def triangular_solve(x, y):
assert len(x.output.shape) == 2
assert len(y.output.shape) == 1 # is this right?
...other assertions...
inputs, x, y = align_tensors(x, y)
data = x.triangular_solve(y)
return Tensor(data, inputs)
# in numpy.py
@eager.register(Binary, TriangularSolveOp, Array, Array)
def triangular_solve(x, y):
... Note it's probably cleanest to add all necessary ops in a separate PR. |
Thank you, @fritzo! I am not familiar with dispatching register so based on your suggestion, I tried to port It seems that your suggestion based on the assumption that |
Well @eb8680 and I had originally envisioned supporting One issue blocking the |
Good point. Probably it is easiest to start with the latter, and make |
I think the easy way to do this is just to write a separate def eager_only(...):
result = eager.dispatch(...)
if result is None:
result = reflect(...)
return result
@eager.register(Binary, AddOp, Gaussian, Gaussian)
@interpretation(eager_only)
def eager_binary_gaussian_gaussian(op, lhs, rhs):
...
The above is a useful fix for this issue, but more generally note that if an expression can be eagerly evaluated as-is then |
@fritzo This should be good to go for now. I'll deal with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to either update or fork tests.test_gaussian.test_fork()
to ensure all ops work with numpy.ndarray
s also. What do you think of adding a backend param like this?
@pytest.mark.parametrize('expr,expected_type', [
('-g1', Gaussian),
...
('g1(x=x0)', (Tensor, Array)), # allow either backend
...
])
@pytest.mark.parametrize("backend", ["torch", "numpy"])
def test_smoke(backend, expr, expected_type):
if backend == "torch":
tensor = torch.tensor
Tensor = Tensor
else:
tensor = np.array
Tensor = Array
...continue with rest of test...
EDIT Or did you have another testing strategy in mind?
I think you'll need to add |
Yes, I'll do that. I think numpy backend will work after I make |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fritzo Smoke tests are passed now. For further gaussian tests, I'll address issues with numpy backend in another PR, which might require changes in other files beside gaussian.py
(I'm not sure). In last commits, I added ops.Tensor
, ops.align_tensor_op
, and ops.materialize
with some changes:
- add
expand
arg to numpy align_array - add
prototype
arg to materialize. The reason is if the input is not Number/Array/Tensor, then we don't know if we need to usetorch.arange
ornumpy.arange
. Soprototype
is helpful to distinguish them. - fix the issue of numpy triangular solve does not support batching.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks mostly good, but we'll need to change align_tensors
and materialize
.
Funsor is organized at varying levels of abstraction. The lowest level of abstraction is ops
which represents pointwise mathematical operations. Higher level than ops
are: domains
and then terms
including free variables and algebra involving ops
. You can think of ops
as arithmetic and terms
as algebra.
While most of the ops you've added are indeed arithmetic operations, the two functions align_tensor
and materialize
involve algebraic concepts of inputs
and free variables. To maintain our abstraction hierarchy, I think a cleaner generic design would be to add @singledispatch
or @multipledispatch
wrappers of these functions in gaussian.py
. Can you find a way to maintain this hieararchy?
Thanks for explaining, @fritzo ! I'll find solutions for them. It should be doable. :) |
|
||
# Permute squashed input dims. | ||
x_keys = tuple(old_inputs) | ||
data = ops.permute(data, tuple(x_keys.index(k) for k in new_inputs if k in old_inputs) + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fritzo I just use dispatch for a hidden _materialize
and add permute
, new_arange
ops so that align_tensor
is backend agnostic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These look great! Do you think we should them into say a funsor/tensor_ops.py, delete the original versions in funsor/torch.py and funsor/numpy.py? That way torch.py, numpy.py and gaussian.py could all import these two generic functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me, we should do so. What is your opinion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's do it. I'm happy merging as is and deferring to a follow-up PR. Or making the change in this PR. Whichever you prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just move that to a follow-up PR. This PR is already getting big 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed! :)
This PR follows Approach 2 in #207 to make Gaussian backend-agnostic. My plan is to
will be
It seems that there is a lot of work to do but I hope that future refactoring will be easier after this.
Tasks
[] subclass Tensor to TorchTensorthis is not necessary for nowstatic methodsops for TorchTensorstatic methodsops in Gaussiangaussian.py
Future PRs
torch._C._get_tracing_state()
statements