Reverse Mode

Reverse Mode reverts the normal control flow, so in this case we will have to seed output variables. This can be a bit counter-intuitive at first, so we provide an additional helper for float arguments.

When marking a float input as Active, we will internally treat it as having a seed of one. So there will be no extra input argument. We will then return the resulting float, either directly, or if your primal function already has a return value, we will update the new function to return a touple, with one additional float per Active input. Those additional float values are in the same order as your Active input float values. If you instead want to seed a float output, you can still mark it as Active. We will then append one float argument to the list of input arguments, which will work as your seed.

More advanced types which are passed by pointer or reference can be marked as Duplicated. In this case a shadow argument will be added. Please note that unlike forward mode, the shadow here will always be mutable, therefore *mut or &mut. This is necessary since we have to overwrite (zero) your seed values for correctness. A motivation for this is given below.

ActivityActiveDuplicatedConst
f32 or f64 InputReturn additional floatN/AUnchanged
&T or &mut T InputN/AAdd &mut T shadowUnchanged
Other Input TypesN/AN/AUnchanged
f32 or f64 OutputAppend float to inputsN/AUnchanged
Other Output TypesN/AN/AUnchanged

Similar to Forward Mode, we offer optimized versions of our Activity Types.

ActivityActiveOnlyDuplicatedOnly
f32 or f64 InputN/AN/A
&T or &mut T InputN/AAccept T, &mut T
f32 or f64 OutputOmit primal return value
Append float to inputs
N/A

ActiveOnly can not yield any optimization for input values, since it is only applicable to floats passed by value. When used on a return type, it has the same effect as DualOnly in Forward Mode. So in the case of fn f(x: f32) -> f32 { x * x }, we would now only return 2.0 * x, instead of (x * x, 2.0 * x), which we would get with Active.

DuplicatedOnly has the same effect as DualOnly on input Arguments. The original input will now be taken by Value, allowing to use it as a scratch space. If the original Type was a mutable reference, we can additionally save the computation of any updates to that value.

Unlike Forward Mode, Reverse Mode will always overwrite (zero) your seed! If you want to call a function generated through Reverse Mode multiple times, you will have to reset your seed values!