A higher order function returning the result based on executing body_fn until cond_fn returns False.
(
cond_fn: Callable[..., torch.Tensor],
body_fn: Callable[..., Tuple[torch.Tensor]],
init_val: pytree.PyTree,
)
| 127 | |
| 128 | |
| 129 | def while_loop( |
| 130 | cond_fn: Callable[..., torch.Tensor], |
| 131 | body_fn: Callable[..., Tuple[torch.Tensor]], |
| 132 | init_val: pytree.PyTree, |
| 133 | ) -> Union[Tuple[torch.Tensor], Value]: |
| 134 | """ |
| 135 | A higher order function returning the result based on executing body_fn |
| 136 | until cond_fn returns False. |
| 137 | """ |
| 138 | flattened_inputs, _ = pytree.tree_flatten(init_val) |
| 139 | if not all(isinstance(i, torch.Tensor) for i in flattened_inputs): |
| 140 | raise ExportError( |
| 141 | ExportErrorType.INVALID_INPUT_TYPE, |
| 142 | f"control_flow.while_loop() expects all inputs values to be tensors, actual inputs: {init_val}", |
| 143 | ) |
| 144 | |
| 145 | with using_tracer(None): |
| 146 | val = init_val |
| 147 | while cond_fn(*val): |
| 148 | val = body_fn(*val) |
| 149 | |
| 150 | flattened_outputs, _ = pytree.tree_flatten(val) |
| 151 | if not all(isinstance(o, torch.Tensor) for o in flattened_outputs): |
| 152 | raise ExportError( |
| 153 | ExportErrorType.INVALID_OUTPUT_TYPE, |
| 154 | f"control_flow.while_loop() expects all returned values to be tensors, actual outputs: {val}", |
| 155 | ) |
| 156 | |
| 157 | tracer = DispatchTracer.get() |
| 158 | |
| 159 | if tracer is None: |
| 160 | return val |
| 161 | |
| 162 | gm_cond = _make_submodule(cond_fn, single_return=True) |
| 163 | gm_body = _make_submodule(body_fn) |
| 164 | |
| 165 | proxies = tuple([unwrap_proxy(v) for v in flattened_inputs]) |
| 166 | |
| 167 | proxy = tracer.create_proxy( |
| 168 | "call_function", |
| 169 | while_loop, |
| 170 | (gm_cond, gm_body, proxies), |
| 171 | {}, |
| 172 | ) |
| 173 | |
| 174 | return tree_return(val, proxy, update_with_proxy) |
| 175 | |
| 176 | |
| 177 | def tracing_context( |
nothing calls this directly
no test coverage detected