# Evaluate gradient (backpropagation) on a user defined type

**URL:** <https://discourse.julialang.org/t/evaluate-gradient-backpropagation-on-a-user-defined-type/74434>\
**Category:** Machine Learning\
**Created:** [January 11, 2022, 9:30pm UTC](https://discourse.julialang.org/t/evaluate-gradient-backpropagation-on-a-user-defined-type/74434 "2022-01-11T21:30:51Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![shiroghost](https://avatars.discourse-cdn.com/v4/letter/s/b9e5f3/32.png) [@shiroghost](https://discourse.julialang.org/u/shiroghost)\
**Post date:** [January 11, 2022, 9:30pm UTC](https://discourse.julialang.org/t/evaluate-gradient-backpropagation-on-a-user-defined-type/74434/1 "2022-01-11T21:30:51Z")

</div>

Hi all,

I have the following problem. Given a function `f(x::Vector)` I know that I can evaluate the gradient using any AD package (In particular I use `ReverseDiff.jl`). What I need now is to evaluate the gradient on a custom type.

For the sake of an example, Imagine that the function is

```julia
f(x) = x^2

```

And that I have a custom type

```julia
struct foo
   x::Float64
   y::Float64
end

```

That has all the custom operations defined (i.e. `*(a::Number,b::foo) = foo(a*b.x,a^2*b.y)`), and something similar for `+,-,/,...` as well as the math intrinsics (`sin,cos,...`).

What I need is to evaluate

```julia
df(x) = 2*x

```

using my “rules” for the defined type `foo` (and therefore returning a type `foo` for the gradient).

Is there a easy way to achieve this, or I would need to hook on to `ChainRule.jl` and construct by hand the forward/backward pass?

Thanks,

PS: For efficiency reasons, I would need to use backpropagation. Forward mode AD is not efficient for my problem.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 11, 2022, 10:24pm UTC](https://discourse.julialang.org/t/evaluate-gradient-backpropagation-on-a-user-defined-type/74434/2 "2022-01-11T22:24:14Z")

</div>

> [@shiroghost](#):
>
> Is there a easy way to achieve this

It works fine with Zygote (which does reverse-mode AD) as long as define `foo` as a type of `Number` and define enough `Number`-like methods:

```julia
struct foo <: Number
    x::Float64
    y::Float64
end
Base.:*(a::foo, b::foo) = foo(a.x*b.x - a.y*b.y, a.y*b.x + a.x*b.y)
Base.:*(x::Real, b::foo) = foo(x*b.x, x*b.y)
Base.one(a::foo) = foo(1,0)
Base.conj(a::foo) = foo(a.x, -a.y)

f(x) = x^2

using Zygote
gradient(f, foo(3,4))

```

gives the correct answer `(foo(6.0, -8.0),)`.

---

<div class="post-metadata">

**Author:** ![shiroghost](https://avatars.discourse-cdn.com/v4/letter/s/b9e5f3/32.png) [@shiroghost](https://discourse.julialang.org/u/shiroghost)\
**Post date:** [January 12, 2022, 6:31pm UTC](https://discourse.julialang.org/t/evaluate-gradient-backpropagation-on-a-user-defined-type/74434/3 "2022-01-12T18:31:18Z")

</div>

Hi,

Many thanks. I cannot manage it to work for a function of several arguments:

```julia
f(x) = x[1]^2 + sin(x[2])*x[1] - 1.0
julia> input = Vector{foo}(undef, 2);
julia> input[1] = foo(6.0, -8.0);
julia> input[2] = foo(1.0, -2.0);
julia> df = x -> gradient(f, x);
julia> df(rand(2))
([0.8613523955990562, 0.09654726844074117],)
julia> df(input)
ERROR: MethodError: no method matching AbstractFloat(::foo)
Closest candidates are:
  (::Type{T})(::T) where T<:Number at boot.jl:760
  (::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number} at char.jl:50
  (::Type{T})(::Base.TwicePrecision) where T<:Number at twiceprecision.jl:243

```

Also does `Zygote` have an in-place version of `gradient`? I have to call this several times, and I would prefer not allocating memory. Any other tips to make `Zygote` fast? I am used to have a pre-compiled tape for running the forward/backward pass more efficiently.

Thanks,

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 12, 2022, 8:55pm UTC](https://discourse.julialang.org/t/evaluate-gradient-backpropagation-on-a-user-defined-type/74434/4 "2022-01-12T20:55:37Z")

</div>

> [@shiroghost](#):
>
> ```julia
> julia> df(input)
> ERROR: MethodError: no method matching AbstractFloat(::foo)
> 
> ```

Define `Base.float(x::foo) = x` (since `foo` is already floating-point in your case, it doesn’t need to do anything… more generally, you’d call `float` recursively on the fields).

Basically, you need to define enough methods for `foo` to act like a number type to satisfy Zygote. Whenever you get a `MethodError`, it’s an indication that you are missing a `Number`-like function. (e.g. you may need to define promotion rules as well).

---

<div class="post-metadata">

**Author:** ![shiroghost](https://avatars.discourse-cdn.com/v4/letter/s/b9e5f3/32.png) [@shiroghost](https://discourse.julialang.org/u/shiroghost)\
**Post date:** [January 19, 2022, 6:20pm UTC](https://discourse.julialang.org/t/evaluate-gradient-backpropagation-on-a-user-defined-type/74434/5 "2022-01-19T18:20:02Z")

</div>

Many thanks,

I have managed `Zygote.jl` to work, but unfortunately is much slower than `ReverseDiff.jl` (even for simple real-valued functions). I have open a new question about this, to see if I am missing anything ([Zygote terribly slow. What am I doing wrong?](https://discourse.julialang.org/t/zygote-terribly-slow-what-am-i-doing-wrong/74877)).

Thanks!
