Finding Derivative of 2 arrays of numbers Using Zygote

Hello,

Please I am working on a physical system, and after running simulations I have 2 arrays of variables like this:

D = [0.01, 0.02, …, 0.05]
m = [1, 2, …, 5]

Please how do I find the derivative (∂D/∂m) using Zygote, given only the above 2 arrays of values. I have checked the docs and I do not understand it. I do not have a function, so I am thinking Zygote could compute the derivative directly.

Thank you in anticipation of your help.

Best,

Hello @alegeaa and welcome!
If you don’t have a function, you cannot compute a derivative. Indeed, the arrays D and m might have been generated by an arbitrary process we know nothing about.
However, what I mean by “function” is quite fuzzy, and it could encompass physical simulators. Do you have access to one or not at all?

1 Like

Hello @gdalle thank YOU FOR YOUR REPLY. I have access to all, i.e. both D and m arrays. I wrote a function to compute the derivative numerically by this formula, ∂D/∂m = [D(m1+delta/2) - D(m1-delta/2)]/ delta. However, I would like to use Zygote for this. Any directions please?

PS: sorry for the late reply.

You can create a function which takes in 2 values and “completes” D and m from that, and then calls your inner function.

1 Like

Thank you Chris, sadly I don’t understand what you mean by “completes”. Could you elaborate please?

Ok, so for instance if you have a function compute_D available, then you can use Zygote as follows:

using Zygote
D = compute_D(m)
JacD = Zygote.jacobian(compute_D, m)[1]
1 Like

I have a function that uses the D and m arrays to compute the differentials for corresponding D and m pairs in the arrays. The function looks like this:

function calc_differentials(delta, diameters, masses)
    diffs = []
    for i in range(1, length(diameters))
        diff = ((diameters[i] * (masses[i] + delta / 2)) - (diameters[i] * (masses[i] - delta / 2))) / delta
        append!(diffs, diff)
    end;
    return diffs
end;

So I understand, you are implying that I do sth like:

for i in range(1, length(diameters))
        JacD = Zygote.jacobian(D[i], m[I])[1]
end

Is this correct?
Thanks

OK, so I think I’m starting to understand your problem. The formula you’re using only makes sense when D is a function that you apply to m and not just a value.

To clarify, let me change notations, and suppose you want to compute the derivative of f: x \longmapsto y. The method of finite differences tells you

f'(x) = \frac{f(x + \delta / 2) - f(x - \delta / 2)}{\delta}

Instead, what you have been computing is the following quantity, which does not tell you anything useful:

\frac{y \times (x + \delta / 2) - y \times (x - \delta / 2)}{\delta}

One reason is that without the function f, you really cannot say anything about the relation between x and y. For instance, consider x = 0 and y = 0:

  • This could be the result of f_1(x) = x, in which case f_1'(x) = 1
  • This could be the result of f_2(x) = -x, in which case f_2'(x) = -1

The same goes if you have several input-ouput pairs (x_i, y_i). You need to have access to the function to differentiate it, whether you use finite differences, symbolic differentiation or automatic differentiation (with Zygote or ForwardDiff)

So my message above tells you why your function calc_differentials is not correct. You need access to the function compute_D if you want to retrieve any useful derivative information.
Supposing you do, then you would compute the derivative at a point m using the following syntax:

i = 3
Zygote.gradient(compute_D, m[i])[1]

I suggested jacobian because I thought compute_D took a vector as input and returned another vector. If the output is a real number, gradient is the way to go.

1 Like

Thank you so much. I understand the solution now. However, my compute_D function returns an array of all the D’s in the simulation. Also, the function has 4 arguments, can I do sth like this:

i = 3
Zygote.gradient(compute_D(arg1, arg2, arg3, arg4), m[i])[1]

or it doesn’t change, i.e,

i = 3
Zygote.gradient(compute_D, m[i])[1]

Thank you, and sorry for my endless questions.

It’s always a good idea to refer to the package documentation for questions about syntax. In this case, if you have a function f(x, y, z), the call

gs = Zygote.gradient(f, x, y, z)

returns a tuple whose first element is the gradient wrt x, second element wrt y, etc.

1 Like

thanks

It returns this error:

MethodError: no method matching zero(::Type{Any})
Closest candidates are:…

That is really hard to debug without a MWE

@alegeaa reading this guide is probably a good idea if you want further help :upside_down_face:

1 Like

Thank you. I will.