From 187796dfd9f6973c2727212c1fe47ee36a69008a Mon Sep 17 00:00:00 2001 From: "Tugsbayasgalan (Tugsuu) Manlaibaatar" Date: Fri, 28 Jul 2023 16:16:19 -0700 Subject: [PATCH] Add basic Export docs (#29) Summary: Pull Request resolved: https://github.com/pytorch/executorch/pull/29 Title Reviewed By: angelayi Differential Revision: D47808548 fbshipit-source-id: 60746960c17be09ffb790402854b532358649be4 --- README.md | 1 + docs/website/docs/export/00_export_manual.md | 27 ++++++ docs/website/docs/export/background.md | 28 ++++++ docs/website/docs/export/constraint_apis.md | 73 +++++++++++++++ docs/website/docs/export/custom_operators.md | 7 ++ docs/website/docs/export/errors.md | 46 +++++++++ .../docs/export/export_api_reference.md | 6 ++ docs/website/docs/export/exportdb.md | 5 + .../docs/export/modules_and_entrypoints.md | 84 +++++++++++++++++ docs/website/docs/export/overall_workflow.md | 21 +++++ docs/website/docs/export/overview.md | 8 ++ docs/website/docs/export/soundness.md | 93 +++++++++++++++++++ docs/website/docs/tutorials/custom_ops.md | 3 +- .../docs/tutorials/exporting_to_executorch.md | 3 + docs/website/sidebars.js | 10 ++ 15 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 docs/website/docs/export/00_export_manual.md create mode 100644 docs/website/docs/export/background.md create mode 100644 docs/website/docs/export/constraint_apis.md create mode 100644 docs/website/docs/export/custom_operators.md create mode 100644 docs/website/docs/export/errors.md create mode 100644 docs/website/docs/export/export_api_reference.md create mode 100644 docs/website/docs/export/exportdb.md create mode 100644 docs/website/docs/export/modules_and_entrypoints.md create mode 100644 docs/website/docs/export/overall_workflow.md create mode 100644 docs/website/docs/export/overview.md create mode 100644 docs/website/docs/export/soundness.md diff --git a/README.md b/README.md index e4091d40249..da458ebcc73 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Compared to the legacy Lite Interpreter, there are some major benefits: - [Setting up ExecuTorch from GitHub](/docs/website/docs/tutorials/00_setting_up_executorch.md) - [Exporting to Executorch](/docs/website/docs/tutorials/exporting_to_executorch.md) - [EXIR Spec](/docs/website/docs/ir_spec/00_exir.md) + - [Exporting manual](/docs/website/docs/export/00_export_manual.md) - [Delegate to a backend](/docs/website/docs/tutorials/backend_delegate.md) - [Executorch Google Colab](https://colab.research.google.com/drive/1oJBt3fj_Tr3FE7L9RdUgSKK9XzJfUv4F#scrollTo=fC4CB3kFhHPJ) diff --git a/docs/website/docs/export/00_export_manual.md b/docs/website/docs/export/00_export_manual.md new file mode 100644 index 00000000000..1f5bb93f271 --- /dev/null +++ b/docs/website/docs/export/00_export_manual.md @@ -0,0 +1,27 @@ +PT2.0 Export Manual + +# Context +At a high level, the goal of PT2 Export is to enable the execution of entire PyTorch programs by other means than the “eager” PyTorch runtime, with a representation that is amenable to meet the optimization and targeting goals of specialized use cases. Specifically, we want to enable users to convert their PyTorch models to a standardized IR, decoupled from its execution, that various domain-specific runtimes can transform and execute independently. This conversion is powered by Dynamo’s technique for sound whole-graph capture—capturing a graph without any “breaks” that would require the eager runtime to fall back to Python. The rest of this wiki documents a snapshot of PT2 Export as of early May 2023—what we consider an "MVP" release. Please note that this project is under active and heavy development: while this snapshot should give a fairly accurate picture of the final state, some some details might change in the coming weeks / months based on feedback. If you have any issues, please file an issue on Github and tag "export". + + +Modules and Entrypoints +Constraints API +Control Flow Operators +Custom Operators +Compiler Passes on Exported Artifact +Non-strict Mode +# Documentation +- [Overview](./overview.md) + - [Background](./background.md) + - [Overall Workflow](./overall_workflow.md) + - [Soundness](./soundness.md) + - [Errors](./errors.md) +- [Export API Reference](./export_api_reference.md) + - [Modules and Entrypoints](./modules_and_entrypoints.md) + - [Constraints API](./constraint_apis.md) + - [Control Flow Operators](../ir_spec/control_flow.md) + - [Custom Operators](./custom_operators.md) +- [Exported Programs](../ir_spec/00_exir.md#exportedprogram) +- [ExportDB](./exportdb.md) + +ir_spec/control_flow diff --git a/docs/website/docs/export/background.md b/docs/website/docs/export/background.md new file mode 100644 index 00000000000..b07346c38e6 --- /dev/null +++ b/docs/website/docs/export/background.md @@ -0,0 +1,28 @@ +

Background

+ +# Setup +Let's say you have a function that you want to export. You export it by passing example inputs (tensors) as arguments to `torch._export.export`. The exported program can then be called with other inputs. +```python +from torch._export import export + +def foo(x): # expect x to be a tensor + ... + +t = torch.rand(4, 8, 32) # example input tensor +exported_foo = export(foo, t) + +# expect that exported_foo can now be called with other input tensor +``` +More generally, the function to be exported can take multiple inputs, and the function itself could be a torch.nn.Module (with a forward method). See [Export API Reference] (./export_api_reference.md). + +# Graph Breaks +The PT2 compiler is a "tracing" compiler, which means that it compiles the execution path—or "trace"—of your function on your example inputs. The intermediate representation of such a trace is a graph. In eager mode it is usual to have graph breaks, where the compiler can fail to trace some parts of the code; this is fine because it can always fall back to the Python interpreter to fill these gaps. However in export mode we do not want any graph breaks: we want the compiler to capture the entire execution in a single graph. + +## Rewriting Code +Graph breaks can arise either because of missing support for Python features, or because the compiler cannot decide which control flow path to continue tracing on. In most cases, it is possible to rewrite code to avoid such graph breaks and complete the export.When the compiler's tracing mechanism does not support some Python feature, we strive to provide a workaround as part of the error message. Over time, we expect to fill in such gaps. On the other hand, not being able to decide which control flow path to continue tracing on is a necessary limitation of the compiler. You are required to use special operators to unblock such cases. See [Control Flow Operators](../ir_spec/control_flow.md). + +# Shapes +Recall that while we need example inputs for export, we must generalize the exported program to be callable with other inputs. The main mechanism for this generalization is through reuse of shapes (of tensors). Next, let us dive deeper into shapes. + +## Static and Dynamic Dimensions +The shape of a tensor is a tuple of dimensions. Roughly speaking, the exported program can be called with inputs of the same shape as the example inputs. By default, we assume that dimensions are static: the compiler assumes they are going to be the same, and specializes the exported program to those dimensions.However, some dimensions, such as a batch dimension, are expected to not be the same—the example inputs passed to export may have a different batch size as inputs to the exported program. Such dimensions must be marked dynamic. See [Soundness](./soundness.md) to learn how to specify properties of such dimensions. diff --git a/docs/website/docs/export/constraint_apis.md b/docs/website/docs/export/constraint_apis.md new file mode 100644 index 00000000000..538f4f7aa82 --- /dev/null +++ b/docs/website/docs/export/constraint_apis.md @@ -0,0 +1,73 @@ +

Constraint APIs

+ +To enable the export of input shape-dependent models, it is necessary for users to impose constraints on the tracing inputs, ensuring the safe traversal of the model during the export process. For a comprehensive understanding of how to utilize this feature, please refer to [this overview](./soundness.md). To express these constraints, we have developed the following set of APIs. + +# dynamic_dim +```python +def dynamic_dim(x: torch.Tensor, dim: int): + """ + Marks the dimension `dim` of input `x` as unbounded dynamic + """ + pass +``` + +It is possible to impose specific bounds on the marked dynamic dimension. For example: +```python +constraints = [dynamic_dim(x, 0) >= 3, dynamic_dim(x, 0) <= 6] +``` + +By passing the above `constraints` to export, we effectively establish the range of acceptable values for the 0th dimension of tensor x, constraining it between 3 and 6. Consequently, the PT2 Export functionality can safely trace through the following program: +```python +def f(x): + if x.shape[0] > 3: + return x.cos() + return x.sin() +``` + +Moreover, it is possible to impose specific equalities between marked dynamic dimensions. For example: +```python +constraints = [dynamic_dim(x, 0) == dynamic_dim(y, 0)] +``` + +This means that whatever the 0th dimensions of tensors x and y may be, they must be the same. This is useful to export the following program, which implicitly requires this condition because of the semantics of `torch.add` + +```python +def f(x, y): + return x + y +``` + +# constrain_as_value +```python +def constrain_as_value(symbol, min: Optional[int], max: Optional[int]): + """ + Adds a minimum and/or maximum constraint on the intermediate symbol during the tracing phase. + """ +``` + +The `constrain_as_value` function informs us that the specified symbol is guaranteed to fall within the provided minimum and maximum values. If no minimum or maximum values are provided, the symbol is assumed to be unbounded. Here's a concrete example of its usage within a model: +```python +def f(x, y): + b = y.item() + constrain_as_value(b, 3, 5) + if b > 3: + return x.cos() + return x.sin() +``` + +# constrain_as_size +```python +def constrain_as_size(symbol, min: Optional[int] = 2, max: Optional[int]): + """ + Adds a minimum and/or maximum constraint on the intermediate symbol during the tracing phase, + with additional checks to ensure the constrained value can be used to construct a tensor shape. + """ +``` + +The `constrain_as_size` API is similar to constrain_as_value but includes additional verifications to ensure that the constrained value can be used to construct a tensor shape. For instance, our tracer specializes in handling shape sizes of 0 or 1, so this API explicitly raises an error if the constrain_as_size is used with a minimum value less than 2. Here's an example of its usage: +```python +def f(x, y): + b = y.item() + constrain_as_size(b, 3, 5) + z = torch.ones(b, 4) + return x.sum() + z.sum() +``` diff --git a/docs/website/docs/export/custom_operators.md b/docs/website/docs/export/custom_operators.md new file mode 100644 index 00000000000..bb914281a2a --- /dev/null +++ b/docs/website/docs/export/custom_operators.md @@ -0,0 +1,7 @@ +

Custom Operators

+ +To ensure a successful export of your model, it is necessary to provide a META implementation for any custom operators used. Custom operators refer to operators that are not part of the "aten" or "prim" namespaces. You have the flexibility to implement the META functionality in either Python or C++. + +Note that the official API for registering custom meta kernels is currently undergoing intensive development. While the final API is being refined, you can refer to the documentation [here](https://docs.google.com/document/d/1GgvOe7C8_NVOMLOCwDaYV1mXXyHMXY7ExoewHqooxrs/edit#heading=h.64r4npvq0w0). In this document, you can find detailed instructions on how to write a Python meta function by searching for the "Out-of-tree" section within the "How to write a Python meta function" section. + +By following the guidelines outlined in the documentation, you can ensure that your custom operators are properly registered and integrated into the export process. We recommend staying updated with our latest announcements and documentation for any updates or improvements to the official API. diff --git a/docs/website/docs/export/errors.md b/docs/website/docs/export/errors.md new file mode 100644 index 00000000000..bbfc794bf5c --- /dev/null +++ b/docs/website/docs/export/errors.md @@ -0,0 +1,46 @@ +

Errors

+ +In this section we discuss errors that can commonly arise during export. + +# Expected Graph Breaks +## Unsupported Features +In PT2 Export, we are primarily reusing the same tracing mechanism—Dynamo—that we use in eager mode. Recall that in eager mode, graph breaks are expected—we always have a fallback option. A consequence of this design is that Dynamo has incomplete coverage of PyTorch and Python features. (That said, the fewer graph breaks there are, generally speaking, the better performance we can expect—even in eager mode—because it enables optimizations to apply over larger regions of code. Thus we are actively working on filling in coverage gaps to avoid graph breaks where possible.)Unfortunately, this means that you may encounter graph breaks during export due to Dynamo coverage gaps. In such cases, you should expect to get an error that includes a link to [ExportDB](./exportdb.md). The corresponding entry should show a minimal negative example (failure) and a minimal positive example (success) that should help you understand the limitation and the workaround, i.e., how to fix the error by rewriting code. + +# Constraint Violations +Recall that you can specify constraints on dynamic dimensions, which encode the soundness conditions for export. It is possible that these constraints are not valid.## Various Cases +Specifically, the compiler may find that: +- A dynamic dimension must be equal to a constant. + - In this case, this dimension must be static: you cannot mark it dynamic. +- A dynamic dimension must be in a range that does not follow the specified range, i.e., is not entirely included between the specified lower and upper bounds. + - In this case, you need to adjust the specified bounds. + - Note that when bounds are not specified, they are implicitly assumed to be [2, infinity). + - For technical reasons that are difficult to explain here, they are assumed to be not 0 or 1. This is not a bug, and does not necessarily mean that your exported program will not work for dimensions 0 or 1. It does mean, though, that you should test for these cases. +- A dynamic dimension must be equal to another dynamic dimension that it is not specified equal to. + - In this case, you need to add the missing equality. + - By default, all dynamic dimensions are assumed to be independent. + - For legacy reasons that are difficult to explain here, you might find spurious implicitly assumed equalities when dimensions in your example inputs happen to be equal. If you ever encounter such a case, please report it as a bug. + +## Using the Compiler as a Guide +See [this overview](./soundness.md/#Constraint Violations and How to Fix Them) of how to fix such errors. Briefly: +* You should see generated functions specializations and specify_constraints on the console that respectively summarize which dimensions are assumed static and what the necessary constraints on the remaining dynamic dimensions are. +* If you agree with this information, you can copy-paste and call specify_constraints with your example inputs to specify constraints, and you can copy-paste and call specializations on your example inputs to assert their constant values. +* If you do not agree and would like to provide tighter constraints, feel free to modify specify_constraints; the compiler will be happy to accept. +* If you do not agree and would like looser constraints, please use TORCH_LOGs=dynamic to enable INFO-level dynamic-shape logging, which will guide you to where the inferred constraints come from. You can also try TORCH_LOGs=+dynamic to enable (further, verbose) DEBUG-level logging. + * Note that you might have to change your code or your expectations based on this information. If you are absolutely convinced that the compiler has a bug, please report it! For example, there are tricky cases where the constraints may come from non-user code, like a fast path in the compiler itself. We encourage you to try different example inputs to avoid such constraints. + +# Missing META Kernels for Operators +## ATen Operators +In the unfortunate case where your model uses an ATen operator that is not supported yet, you may get an obscure error of the form: +```python +Unable to find op(FakeTensor, FakeTensor, ...) +``` +Please report a bug if you encounter this error + +## Custom Operators +In this case you should follow the instructions at [Custom Operators](./custom_operators.md). Note that the current mechanism is not ideal, but will be updated soon to make it easy for you to register custom operators. + +# Validation Errors +Note that we do not do any validation of the exported program yet; this is planned for the near future.I n these cases you should report a bug since the issue is likely in PyTorch. +## Correctness +The export workflow should complain when the exported program behaves differently than the eager program by running the example inputs through both. ## Serialization roundtrip failure +The export workflow should serialize and deserialize the exported program, and then run the correctness test again. diff --git a/docs/website/docs/export/export_api_reference.md b/docs/website/docs/export/export_api_reference.md new file mode 100644 index 00000000000..1de0cd87149 --- /dev/null +++ b/docs/website/docs/export/export_api_reference.md @@ -0,0 +1,6 @@ +

Export API Reference

+ +- [Modules and Entrypoints] (./modules_and_entrypoints.md) +- [Constraints API] (./constraint_apis.md) +- [Control Flow Operators] (../ir_spec/control_flow.md) +- [Custom Operators] (./custom_operators.md) diff --git a/docs/website/docs/export/exportdb.md b/docs/website/docs/export/exportdb.md new file mode 100644 index 00000000000..71f5b56c41b --- /dev/null +++ b/docs/website/docs/export/exportdb.md @@ -0,0 +1,5 @@ +

ExportDB

+ +ExportDB is a [centralized] (https://pytorch.org/docs/main/generated/exportdb/index.html) dataset for recording working and non working export usage examples. + +We try to cover a lot of corner cases from PyTorch and Python so that users can have a better understanding of the capabilities and caveats of export, by browsing the example database. We also expect that errors that arise during export will have actionable messages that point to examples in this database. diff --git a/docs/website/docs/export/modules_and_entrypoints.md b/docs/website/docs/export/modules_and_entrypoints.md new file mode 100644 index 00000000000..910192bc8bd --- /dev/null +++ b/docs/website/docs/export/modules_and_entrypoints.md @@ -0,0 +1,84 @@ +

Modules and Entrypoints

+ + Disclaimer: Please note that at present, we do not offer any backward compatibility guarantees for the following APIs. While we are committed to minimizing significant API changes, it is important to understand that we are currently in an intensive development phase, and as such, we reserve the right to modify implementation details and top-level API parameters. We are constantly striving to enhance our offerings and deliver the best possible experience to our users. However, during this phase, it is essential to remain aware that certain adjustments may be necessary to improve functionality, stability, or meet evolving requirements. + +# Export API + +At the top level, the export API is defined as follows: + +```python +def export( + m: Union[torch.nn.Module, Callable[..., Any]], + args: Union[Dict[str, Tuple[Value, ...]], Tuple[Value, ...]], + constraints: Optional[List[Constraint]] = None, +) -> ExportedProgram: + """ + Traces either an nn.Module's forward function or just a callable with PyTorch + operations inside and produce a ExportedProgram. + + Args: + m: the `nn.Module` or callable to trace. + + args: Tracing example inputs. + + constraints: A list of constraints on the dynamic arguments specifying + their possible range of their shapes + + Returns: + An ExportedProgram containing the traced method. + """ +``` + +# Exported Artifact +The export call returns a custom export artifact called ExportedProgram: + +```python +class ExportedProgram: + graph_module: torch.fx.GraphModule + graph_signature: ExportGraphSignature + call_spec: CallSpec + state_dict: Dict[str, Any] + symbol_to_range: Dict[sympy.Symbol, Tuple[int, int]] + + @property + def graph(self): + return self.graph_module.graph + + def transform(self, *passes: PassType) -> "ExportedProgram": + # Runs graph based transformations on the given ExportedProgram + # and returns a new transformed ExportedProgram + ... + + def add_runtime_assertions(self) -> "ExportedProgram": + # Adds runtime assertions based on the constraints + ... + +# Information to maintain user calling/returning specs +@dataclasses.dataclass +class CallSpec: + in_spec: Optional[pytree.TreeSpec] = None + out_spec: Optional[pytree.TreeSpec] = None + + +# Extra information for joint graphs +@dataclasses.dataclass +class ExportBackwardSignature: + gradients_to_parameters: Dict[str, str] + gradients_to_user_inputs: Dict[str, str] + loss_output: str + + +@dataclasses.dataclass +class ExportGraphSignature: + parameters: List[str] + buffers: List[str] + + user_inputs: List[str] + user_outputs: List[str] + inputs_to_parameters: Dict[str, str] + inputs_to_buffers: Dict[str, str] + + buffers_to_mutate: Dict[str, str] + + backward_signature: Optional[ExportBackwardSignature] +``` diff --git a/docs/website/docs/export/overall_workflow.md b/docs/website/docs/export/overall_workflow.md new file mode 100644 index 00000000000..23d75bc93c6 --- /dev/null +++ b/docs/website/docs/export/overall_workflow.md @@ -0,0 +1,21 @@ +

Overall Workflow

+ +# Step 0: Preparation +To export a model, you need to ensure that: +- You have some example inputs you expect to work for your model. +- You are able to rewrite some of the model's code as necessary to successfully capture a single graph. See [Graph Breaks](./background.md/#graph-breaks). (NOTE: We do not have a story for how to deal with graph breaks in third-party libraries.) +- You know which shape dimensions (if any) of the model's inputs should be dynamic. See [Shapes](./background.md/#shapes). + +# Step 1: Specification +Next, you express which dimensions (if any) you expect to be dynamic, and (optionally) specify constraints on them to the best of your knowledge. (The compiler will guide you on whether your constraints are sufficient, so if you do not know anything, it's fine. However, the compiler will not infer which dimensions should be dynamic.) These constraints encode conditions for soundness of the exported program. See [Soundness](./soundness.md). + +# Step 2: Trial and Error +You now call export on your model with your example inputs and constraints. See [Export API Reference](./export_api_reference.md). At this point you may hit various kinds of errors—typically, due to graph breaks or insufficient constraints. See [Errors](./errors.md). We expect that the error messages should be actionable, so you can learn how to fix them—usually, by looking at linked examples in [ExportDB](./exportdb.md)—and fix them by rewriting code. + +# Step 3. Inspection +At this point you will have an exported program, and you will be warned about the assertions it makes on inputs: in particular, which dimensions are static and have been specialized, and what conditions are expected on the remaining dynamic dimensions. Make sure they make sense; otherwise you should debug them. + +# Step 4: Testing +Finally, we would encourage you to try out other inputs whose shapes are valid—i.e., they satisfy the assertions emitted by the compiler—yet different from the example inputs you provided to export. (This only makes sense if you had some dynamic dimensions.)* If all the inputs you try pass, great! Consider your workflow complete. +* Otherwise, you have hit what is almost surely a over-specialization bug. Please file a bug on github with a pointer to your model, which example inputs and constraints you used for export, and which inputs it failed on. We will try to unblock you as best as possible. +## diff --git a/docs/website/docs/export/overview.md b/docs/website/docs/export/overview.md new file mode 100644 index 00000000000..85f2c0a04d7 --- /dev/null +++ b/docs/website/docs/export/overview.md @@ -0,0 +1,8 @@ +# Overview + +We begin with a brief overview of the experience of using PT2 Export and the key concepts in its design. + +- Background [./background.md] +- Overall Workflow [./overall_workflow.md] +- Soundness [./soundness.md] +- Errors [./errors.md] diff --git a/docs/website/docs/export/soundness.md b/docs/website/docs/export/soundness.md new file mode 100644 index 00000000000..6ddc6703b89 --- /dev/null +++ b/docs/website/docs/export/soundness.md @@ -0,0 +1,93 @@ +

Soundness

+ +The main mechanism to ensure the correctness of the exported program when called with other inputs is the specification of constraints on the shapes of inputs. Concretely, dimensions that are static are specialized, and other dimensions must be marked dynamic, along with any constraints they must satisfy. This information is then converted into assertions on each dimension that other inputs must satisfy. +# Specifying Constraints +To mark a dimension dynamic, you pass the relevant example input tensor and dimension to torch._export.dynamic_dim. It is possible to specify bounds on and equalities between such dimensions. In particular, you can use Python relational operators to provide: +- expected upper and lower bounds of dynamic dimensions; +- expected equalities between dynamic dimensions. + +You then pass these specifications as constraints to export. +```python +from torch._export import dynamic_dim, export + +def foo(x): # expect x to be a tensor + ... + +t = torch.rand(4, 8, 32) # example input tensor +# mark some dimensions of input tensor to be dynamic (assumed static by default) +constraints = [ + dynamic_dim(t, 0), + dynamic_dim(t, 1) <= 256, +] +exported_foo = export(foo, t, constraints=constraints) + +# expect that exported_foo can now be called with other input tensors +# and constraints encode conditions on such input tensors for correctness +``` +Note that dynamic dimensions are tracked "symbolically" by the compiler—for correctness, it cannot use their "concrete" values in example inputs in the exported program, but only the specified constraints on them. When the compiler finds any additional necessary conditions on them as it traces through the code, it reports them back as part of a ConstraintViolationError. Next, let us look at how to fix such an error. + +# Constraint Violations and How to Fix Them +Usually you will have some idea of which dimensions you want to be dynamic, and what bounds you want on them. But suppose that you want the compiler to guide you. In that case, just specify what you think is reasonable—the compiler will emit actionable error messages where needed. In the limit, you can specify all dimensions to be dynamic, with no bounds, and see where that leads! +```python + +from torch._export import dynamic_dim, export + +def foo(x): # expect x to be a tensor + ... + +t = torch.rand(4, 8, 32) # example input tensor +# I want the compiler to guide me on what ranges to specify +constraints = [dynamic_dim(t, i) for i in range(t.dim())] +exported_foo = export(foo, t, constraints=constraints) +``` +Suppose that when tracing the code, the compiler finds that dimension 1 must have a non-trivial upper bound and dimension 2 must be a constant. The compiler will emit an error of the following form: + +```python +torch.fx.experimental.symbolic_shapes.ConstraintViolationError: Constraints violated! + ... + +The following dimensions have been specialized. They CANNOT be dynamic. +def specializations(x): + return x.size()[2] == 32 + +The following dimensions CAN be dynamic. Here’s how to specify constraints on them: +def specify_constraints(x): + return [ + dynamic_dim(x, 0), + dynamic_dim(x, 1) <= 256, + ] +``` +In other words, this error means that: +- Dimension 2 of the input was found to be constrained to be 32. The generated code will assume that `x.size()[2] == 32`, possibly use this value for specialization, and will assert this condition on other inputs. +- Dimension 0 and 1 of the input can range over different values. Moreover, dimension 1 cannot be more than 256. The generated code will assume that `x.size()[1] <= 256`, possibly use this upper bound for memory planning, and will assert this condition on other inputs. + +At this point, you are free to use these discovered facts as you choose for the final specification:* You may use them "as is." +- You may include further knowledge based on the intended use of the exported program, such as: + - upper-bounding dimension 0, say with 1024; + - tightening the upper bound on dimension 1, say with 128; + - deciding that one or both should not be considered dynamic: you do this by taking them out of constraints, effectively asking the compiler to specialize on their concrete value in the input. + +Or you may be surprised and want to dig in further, to try to find out why the compiler discovered these facts. For that, you can re-run the export script with prefix TORCH_LOGS=dynamic,dynamo on the command line.) You will see log messages such as the following: +```python +[INFO] creating symbol: s0 = 4 with source: x.size()[0] +[INFO] creating symbol: s1 = ... x.size()[1] +[INFO] creating symbol: s2 = ... x.size()[2] + +[INFO] adding guard: s1 <= s2 * 8 at: +File "example.py", line 629, in foo + if x.shape[1] <= x.shape[2] * 8: +[INFO] adding guard: s1 * 4 <= 2048 at: ... +[INFO] adding guard: s2 // 8 >= 2 at: ... +[INFO] adding guard: s2 * s2 + s1 * 8 <= 4096 at: ... +[INFO] adding guard: s2 % 4 == 0 at: ... +[INFO] adding guard: s2 * 2 == 64 at: ... +[INFO] Summary of dimension constraints: +The following dimensions have been specialized and CANNOT be dynamic. + +def specializations(x: torch.Tensor): + assert x.size()[0] == 2 + assert x.size()[1] == 2 + +``` +Under the hood, the compiler creates symbols for each dimension—in this case, (s0, s1, s2)—and generates conditions involving these symbols. For example, the condition s1 <= s2 * 8 is generated when tracing the shown line of code in function foo. You can also see a list of specializations we have assumed on input shapes. This can give you an idea of how your code led to the individual pieces of information being discovered, which were ultimately simplified to produce the final error message. +# diff --git a/docs/website/docs/tutorials/custom_ops.md b/docs/website/docs/tutorials/custom_ops.md index e46b98c6c56..2ec77580243 100644 --- a/docs/website/docs/tutorials/custom_ops.md +++ b/docs/website/docs/tutorials/custom_ops.md @@ -23,7 +23,8 @@ In a nutshell, we need the following steps in order for a custom op to work on E 1. Register the custom op definition into PyTorch runtime so that they are visible to Executorch compiler. 2. Implement and register the implementation of it to PyTorch runtime. Do one of the following: 1. Implement in Python and use [`library.py`](https://github.com/pytorch/pytorch/blob/main/torch/library.py) API to register it - 2. Implement in C++, use `at::Tensor` or `exec_aten::Tensor` and use [`library.h`](https://github.com/pytorch/pytorch/blob/main/torch/library.h) API to register it + 2. Implement in C++, use `at::Tensor` or `exec_aten::Tensor` and use [`library.h`](https://github.com/pytorch/pytorch/blob/main/torch/library.h) API to register it. + 3. You will also need to provide META implementatio for your custom operator, so that export tracer knows how to reason about the input/output of your custom operator. For more instructions, you can refer [here](../export/custom_operators.md) 3. Define an out variant of the custom op, implement it in C++ using `ETensor`. This step should also be trivial if we used `exec_aten::Tensor` in step 2.ii, since we can share the same logic for the two variants. 4. Create `custom_ops.yaml` for this operator, both functional and out variant, specify function schema and corresponding kernels. (See Common APIs for more info). 1. In ATen mode, the C++ kernel implementation using `at::Tensor` will be linked. diff --git a/docs/website/docs/tutorials/exporting_to_executorch.md b/docs/website/docs/tutorials/exporting_to_executorch.md index d0b71fcc81e..1f4357fa01d 100644 --- a/docs/website/docs/tutorials/exporting_to_executorch.md +++ b/docs/website/docs/tutorials/exporting_to_executorch.md @@ -47,6 +47,9 @@ is supported in the export flow, you can take a look at the examples in through registering the custom operator to a torch library and providing a meta kernel. +To learn more about exporting a model or if you have trouble exporting, +you can look at [these docs](../export/00_export_manual.md) + The output of `exir.capture` is a fully flattened graph (meaning the graph does not contain any module heirachy, except in the case of control flow operators) containing the diff --git a/docs/website/sidebars.js b/docs/website/sidebars.js index e3650801ef5..429cf9faec2 100644 --- a/docs/website/sidebars.js +++ b/docs/website/sidebars.js @@ -60,6 +60,16 @@ module.exports = { }, ] }, + { + type: "category", + label: "Export", + items: [ + { + type: "autogenerated", + dirName: "export", + }, + ] + }, { type: "category", label: "For Contributors",