For explicit function arguments, you can write it like this:
grad[f_[args__]] := Table[D[f[args], a], {a, {args}}]
This does exactly what you want. For example:
grad[f[x, y]]
(* {(f^(1,0))[x,y],(f^(0,1))[x,y]} *)
and
grad[f[x, y, z, w]]
(* {(f^(1,0,0,0))[x,y,z,w],(f^(0,1,0,0))[x,y,z,w],(f^(0,0,1,0))[x,y,z,w],(f^(0,0,0,1))[x,y,z,w]} *)
The trick is the args__
pattern, which means that args
becomes the Sequence
of arguments, no matter how long it is (but there must be at least one argument). You can convert this Sequence
into a List
by writing {args}
. This you can then use, e.g., in a Table
the way I did. For completeness: the "dimension" of the argument function is Length[{args}]
.
Addendum
If you want a gradient operator instead, you could write
gradOp[f_[args__]] := With[{dim = Length[{args}]},
Table[Derivative[Sequence@@UnitVector[dim, n]][f][args], {n, dim}]
]
such that
gradOp@f[12, y]
(* {(f^(1,0))[12,y],(f^(0,1))[12,y]} *)
You can even make this operator work with pure functions, as long as they don't contain a SlotSequence
. First, make sure the above definition does not evaluate for pure function arguments, using /;
.
ClearAll[gradOp];
gradOp[f_[args__]] := With[{dim = Length[{args}]},
Table[Derivative[Sequence @@ UnitVector[dim, n]][f][args], {n, dim}]
] /; FreeQ[f[args], Slot[_] | SlotSequence[___]]
Then, define the case of a pure-function argument.
gradOp[f_Function] := Module[{C},
(* extract all slot names in f *)
With[{slots = Cases[f, Slot[i_] :> i, \[Infinity]]},
(* replace the slots by variables *)
With[{expr = f /. Thread[Thread[Slot[slots]] -> Thread[C[slots]]] /. Function -> Identity},
(* construct a new pure function... *)
Activate@Inactive[Function][
(* ...which represents the gradient of f *)
Table[D[expr, a], {a, Thread[C[slots]]}] /. Thread[Thread[C[slots]] -> Thread[Slot[slots]]]
]
]
]
] /; FreeQ[f, SlotSequence[___]]
With this gradOp
works on both, explicit and pure functions.
gradOp@f[x]
(* {f'[x]} *)
gradOp[#1 - #2^2 &]
(* {1, -2 #2} & *)
gradOp[#1 - #2^2 &][x, y]
(* {1, -2 y} *)
It even keeps the slot names:
gradOp[Log[#xyz] + #test^2 &]
(* {1/#xyz, 2 #test}& *)