```
library(tensorflow)
library(keras)
library(tfautograph)
tf$version$VERSION
#> [1] "2.6.0"
```

`autograph()`

works without needing to provide any hints. It can automatically translate expressions like `if`

and `while`

into `tf.cond()`

and `tf.while_loop()`

calls.

However, compared to the flexibility of R control flow, the tensorflow control flow functions have much narrower constraints on how the control flow nodes are constructed. The constraints of functions like `tf.cond()`

, `tf.while_loop()`

and `dataset.reduct()`

require that `autograph()`

do some sophisticated inference from the R code to anticipate what the outcome will be from evaluating a block of code, so that it can “fix-up” the outcome to satisfy the constraints tensorflow sets out.

If you want to bypass these inference routines, (for example, because the inferred outcome is producing a graph that has more nodes than strictly necessary, or for some minor performance benefits), you can provide hints to `autograph`

to let it know what it would otherwise try to infer.

There are 2 main types of hints, corresponding to the two main control flow functions: `if`

and `while`

.

Since these hints are only relevant in graph mode we’ll disable eager execution for the rest of the vignette. In your code you’re more likely to enter graph mode only when tracing a `tf.function()`

.

```
tf$compat$v1$disable_eager_execution()
sess <- tf$compat$v1$Session()
```

`if`

Take the following expression:

```
x <- tf$compat$v1$placeholder('float32')
y <- tf$constant(0)
```

```
y
#> Tensor("Const:0", shape=(), dtype=float32)
autograph({
if(x > 0)
y <- y + 1
})
y
#> Tensor("cond/Identity:0", shape=(), dtype=float32)
```

Notice that `y`

points to a different tensor after the `autograph()`

call. The new tensor has `'cond'`

in the name, which is a big hint that `autograph()`

took that expression and called `tf.cond()`

with it.

If you were to inspect the graph (e.g, by reading the `GraphDef`

or inspecting it in tensorboard, perhaps by calling `tfautograph::view_function_graph()`

), you would see that in essence, the expression got translated into the equivalent of:

```
y <- tf$cond(x > 0,
true_fn = function() y + 1,
false_fn = function() y)
```

`tf.cond()`

takes the true and false branches as functions `true_fn`

and `false_fn`

, and requires that the two functions return the same structure of tensors.

How did `autograph()`

know how to balance the two branches? It traced each branch as a `tf.function`

and then reconciled the differences. Then within the `tf.cond()`

call it recalled the traced function of the branch, along with some routines that “fix-up” the actual outcome from the branch into a balanced final outcome.

If you wanted to avoid tracing each branch as a separate `ConcreteFunction`

and balancing the output structures, you could supply a hint to autograph about what outcomes you expect from the branches by calling `ag_if_vars()`

right before the `if`

.

```
autograph({
ag_if_vars(y)
if(x > 0)
y <- y + 1
})
```

Here, `y`

is the only variable modified, so it’s the only symbol that we want returned from the `tf.cond()`

.

`ag_if_vars()`

can help you specify a few different types of outcomes that should be captured by the `tf.cond()`

besides modified symbols, including modified values in nested structures, return values, number of control flow events (e.g., `break`

and/or `next`

statements), and local variables.

```
args(ag_if_vars)
#> function (..., modified = list(), return = FALSE, undefs = NULL,
#> control_flow = 0)
#> NULL
```

see `ag_if_vars()`

for details.

`while`

and `for`

Take the expression:

```
x <- tf$constant(0)
n <- tf$constant(10)
autograph({
while(n > 0) {
x <- x + 1
n <- n - 1
}
})
```

If you were to build the equivalent with `tf.while_loop()`

call it would look like this:

```
c(x, n) %<-% tf$while_loop(
cond = function(x, n)
n > 0,
body = function(x, n) {
x <- x + 1
n <- n - 1
tuple(x, n)
},
loop_vars = tuple(x, n)
)
```

So, as you can see there are a few things that `autograph`

needs to know to be able to translate an R `while`

loop into a `tf.while_loop()`

call. Namely, it needs to know what are the `loop_vars`

that might be modified by the loop body, both so that it can pass them to the `loop_vars`

argument and so that it can construct `cond`

and `body`

functions with the appropriate formals and return values.

How does `autograph()`

know which symbols belong in `loop_vars`

, which are meant to be local-only variables, and which are only read and never modified? There are a couple different ways to approach solving this, but the way `autograph()`

does it by statically inferring them by examining the R loop body expression. That is, without actually evaluating the code, we inspect which symbols are the targets of common assignment operators like `<-`

, `->`

, `=`

, `%<-%`

, `%->%`

and `%<>%`

. If you are autographing a `for`

loop like `for(var in seq)`

than the symbol `var`

is included in the set of symbols that would be statically inferred as assignment targets.

After all the assigned symbols are inferred, they are classified as either belonging to `loop_vars`

or `undefs`

based on whether the symbol already exists.

This approach is quicker and cheaper than tracing, but it can sometimes lead to some symbols to `loop_vars`

that otherwise could have been to be local-only variables.

You can provide hints to autograph about what are the `loop_vars`

like so:

```
autograph({
ag_loop_vars(x, n)
while(n > 0) {
x <- x + 1
n <- n - 1
}
})
```

Because the most common motivation for providing a loop hint will be to exclude a certain symbol from being captured as a loop var, there is special syntax for that. Prefix the variable with a minus `-`

like so:

```
tmp <- tf$constant(99)
autograph({
ag_loop_vars(-tmp)
while(n > 0) {
tmp <- x + 1
x <- tmp
n <- n - 1
}
})
```

Note that by default `ag_loop_vars`

exports excluded variables as `undefs`

, symbols that throw an error (with a helpful error message) if you try to access them. To prevent `autograph`

from creating undefs you can pass NULL like so:

`ag_loop_vars(-tmp, undefs = NULL)`

You can also use `ag_loop_vars()`

to specify additional symbols to be included with the statically inferred ones by prefixing them with a `+`

. See `?ag_loop_vars()`

for additional examples.