Skip to content

Commit 2d75b4b

Browse files
mcabbottoxinabox
andauthored
Update which_functions_need_rules.md (#554)
* Update which_functions_need_rules.md * Update which_functions_need_rules.md * Update which_functions_need_rules.md * Apply suggestions from code review Co-authored-by: Frames Catherine White <oxinabox@ucc.asn.au> * comma Co-authored-by: Frames Catherine White <oxinabox@ucc.asn.au>
1 parent c979ab7 commit 2d75b4b

File tree

1 file changed

+19
-9
lines changed

1 file changed

+19
-9
lines changed

docs/src/rule_author/which_functions_need_rules.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,40 @@ Other patterns can be AD'ed through, but the backward pass performance can be gr
2727
#### Functions which mutate arrays
2828
For example,
2929
```julia
30-
function addone!(array)
31-
array .+= 1
32-
return sum(array)
30+
function addone(a::AbstractArray)
31+
b = similar(a)
32+
b .= a .+ 1
33+
return sum(b)
3334
end
3435
```
3536
complains that
3637
```julia
3738
julia> using Zygote
38-
julia> gradient(addone!, a)
39+
julia> gradient(addone, a)
3940
ERROR: Mutating arrays is not supported
4041
```
4142
However, upon adding the `rrule` (restart the REPL after calling `gradient`)
4243
```julia
43-
function ChainRules.rrule(::typeof(addone!), a)
44-
y = addone!(a)
45-
function addone!_pullback(ȳ)
44+
function ChainRules.rrule(::typeof(addone), a)
45+
y = addone(a)
46+
function addone_pullback(ȳ)
4647
return NoTangent(), ones(length(a))
4748
end
48-
return y, addone!_pullback
49+
return y, addone_pullback
4950
end
5051
```
5152
the gradient can be evaluated:
5253
```julia
53-
julia> gradient(addone!, a)
54+
julia> gradient(addone, a)
5455
([1.0, 1.0, 1.0],)
5556
```
57+
Notice that `addone(a)` mutates another array `b` internally, but **not** its input.
58+
This is commonly done in less trivial functions, and is often what Zygote's `Mutating arrays is not supported` error is telling you,
59+
even though you did not intend to mutate anything.
60+
Functions which mutate their own input are much more problematic.
61+
These are the ones named (by convention) with an exclamation mark, such as `fill!(a, x)` or `push!(a, x)`.
62+
It is not possible to write rules which handle all uses of such a function correctly, on current Zygote.
63+
5664

5765
!!! note "Why restarting REPL after calling `gradient`?"
5866
When `gradient` is called in `Zygote` for a function with no `rrule` defined, a backward pass for the function call is generated and cached.
@@ -61,6 +69,8 @@ julia> gradient(addone!, a)
6169
If an `rrule` is defined before the first call to `gradient` it should register the rule and use it, but that prevents comparing what happens before and after the `rrule` is defined.
6270
To compare both versions with and without an `rrule` in the REPL simultaneously, define a function `f(x) = <body>` (no `rrule`), another function `f_cr(x) = f(x)`, and an `rrule` for `f_cr`.
6371

72+
Calling `Zygote.refresh()` will often have the same effect as restarting the REPL.
73+
6474
#### Exception handling
6575

6676
Zygote does not support differentiating through `try`/`catch` statements.

0 commit comments

Comments
 (0)