tensorplay.library
tensorplay.library is a collection of APIs for extending TensorPlay’s core library of operators. It contains utilities for testing custom operators, creating new custom operators, and extending operators defined with TensorPlay’s C++ operator registration APIs (e.g. built-in operators). For a detailed guide on effectively using these APIs, please see TensorPlay Custom Operators Landing Page for more details on how to effectively use these APIs.
Testing custom ops
Use tensorplay.library.opcheck() to test custom ops for incorrect usage of the
Python tensorplay.library and/or C++ TORCH_LIBRARY APIs. Also, if your operator supports
training, use tensorplay.autograd.gradcheck() to test that the gradients are
mathematically correct.
Runs each selected check and reports failures keyed by test name: |
Creating new custom ops in Python
Use tensorplay.library.custom_op() to create new custom ops.
Choosing the kind of custom op
When defining a custom op, first decide what aliasing and mutation behavior the
operator has. This choice determines the mutates_args, tags, return schema,
and fake-kernel behavior.
Does the operator mutate any Tensor input?
|
+-- no --> Functional operator
| Use mutates_args=().
| Return new Tensor objects that do not alias inputs or each other.
|
+-- yes --> Does it mutate the first positional Tensor argument and return it?
|
+-- yes --> In-place operator
| Use tags=tensorplay.Tag.inplace.
| Use mutates_args with exactly the first argument name.
| Return that same first argument.
|
+-- no --> Does it mutate keyword-only Tensor out= arguments and return them?
|
+-- yes --> out= operator
| Use tags=tensorplay.Tag.out.
| Do not read from the out= tensors.
| Put mutable output tensors after * as keyword-only args.
| Return all out= tensors in declaration order.
|
+-- no --> Mutable operator
Use mutates_args for the mutated arguments.
Do not return mutated inputs or aliases of inputs.
Functional operators are the simplest and should be preferred when possible.
They should not mutate inputs, and their outputs should be fresh values that do
not alias any input or other output.
Use tags=tensorplay.Tag.inplace only for the conventional in-place shape: the first
positional argument is a Tensor, it is the only mutated argument, and the
operator returns that same Tensor. For example, an inferred in-place custom op
has a schema shaped like:
(Tensor(a!) x, ...) -> Tensor(a!)
Use tags=tensorplay.Tag.out only for conventional out= variants: all mutable
output tensors are keyword-only arguments, and the operator returns those
outputs in declaration order. The implementation must write to out= tensors,
but must not read from them. For example:
(Tensor x, *, Tensor(a!) out) -> Tensor(a!)
(Tensor x, *, Tensor(a!) out0, Tensor(b!) out1) -> (Tensor(a!), Tensor(b!))
In-place and out= custom ops get an autogenerated fake kernel from their tag
and schema. Registering a fake kernel for these ops is usually unnecessary,
though it is still allowed if you need to override the autogenerated behavior.
out= custom ops follow the same autograd rule as built-in out= operators:
they do not support automatic differentiation when any argument requires grad.
Arbitrary aliasing, such as returning an alias of an input that is not one of
the in-place or out= patterns above, is not supported by custom_op.
TensorPlay compiler transforms such as functionalization need to reason precisely
about mutations and aliases, so custom op aliasing is intentionally limited to
the conventional forms described here. If your operator mutates inputs but is
not an in-place or out= operator, model it as a mutable operator and return no
aliases of inputs.
The same operation can have different contracts depending on how it handles its
outputs:
from tensorplay import Tensor
@tensorplay.library.custom_op(
"mylib::add_inplace",
mutates_args={"x"},
tags=tensorplay.Tag.inplace,
)
def add_inplace(x: Tensor, y: Tensor) -> Tensor:
x.add_(y)
return x
@tensorplay.library.custom_op(
"mylib::add_out",
mutates_args={"out"},
tags=tensorplay.Tag.out,
)
def add_out(x: Tensor, y: Tensor, *, out: Tensor) -> Tensor:
out.copy_(x + y)
return out
@tensorplay.library.custom_op("mylib::add_mutate", mutates_args={"x"})
def add_mutate(x: Tensor, y: Tensor) -> None:
x.add_(y)
Note
More details on mutation, aliasing, and transforms
custom_op asks for a precise mutation and aliasing contract because TensorPlay
uses that contract in FakeTensor, autograd, functionalization, and
tensorplay.compile.
For a functional custom op, TensorPlay assumes the operator does not mutate any input and that returned tensors are fresh values. This is the easiest kind of op for TensorPlay transforms to reason about.
For an in-place custom op, tensorplay.Tag.inplace gives TensorPlay a stronger and more
specific contract: the first Tensor argument is mutated and the returned Tensor
is the same object. This lets TensorPlay derive the fake behavior from the schema
instead of requiring a separate fake kernel.
For an out= custom op, tensorplay.Tag.out gives TensorPlay the corresponding
out=-variant contract: keyword-only output tensors are mutated and returned in
schema order. The out= tensors are output buffers only: the implementation
must not use their current values as inputs. Like built-in out= operators,
these custom ops do not support automatic differentiation when any argument
requires grad.
For other mutable custom ops, use mutates_args to name the mutated arguments,
but do not return mutated inputs or aliases of inputs. Arbitrary aliasing is
hard for transforms because functionalization rewrites mutating programs into
functional programs and must know exactly which returned tensors share storage
with which inputs. custom_op therefore supports the conventional in-place and
out= aliasing patterns directly, but does not model arbitrary view or alias
relationships.
Example. |
|
The registered kernel(s) must launch their Triton kernels through |
|
Mark a Triton kernel as launchable from within a |
Extending custom ops (created from Python or C++)
Use the register.* methods, such as tensorplay.library.register_kernel() and
tensorplay.library.register_fake(), to add implementations
for any operators (they may have been created using tensorplay.library.custom_op() or
via TensorPlay’s C++ operator registration APIs).
Accepts a |
|
Produces |
|
|
Low-level APIs
The following APIs are direct bindings to TensorPlay’s C++ low-level operator registration APIs.
Warning
The low-level operator registration APIs and the TensorPlay Dispatcher are a complicated TensorPlay concept. We recommend you use the higher level APIs above (that do not require a tensorplay.library.Library object) when possible. This blog post is a good starting point to learn about the TensorPlay Dispatcher.
A tutorial that walks you through some examples on how to use this API is available on Google Colab.
one DEF library per process/namespace), |
|
Creates the |
|
|

