# Why is NHoldFirst not propagated to symbolic derivatives?

I encountered a nasty problem that N cannot evaluate expressions containing a symbolic Derivative of a multi-parameter function expecting an exact integer as its first argument.

Attributes[StieltjesGamma]
(*
*)

d = Derivative[0, 1][StieltjesGamma][1, 1]
(*
Derivative[0, 1][StieltjesGamma][1, 1]
*)

N[d, 10]
(*
StieltjesGamma::intnm: Non-negative machine-sized integer expected at position 1
in StieltjesGamma[1.000000000,1.000000000]. >>
Derivative[0, 1][StieltjesGamma][1.000000000, 1.000000000]
*)


The first argument of StieltjesGamma only makes sense when it is an integer, and NHoldFirst is supposed to take care of this when approximate numeric calculations are performed. But in this case it goes unnoticed by N that results in a meaningless expression and an error message.

This also blocks some symbolic simplifications that internally rely on arbitrary-precision numeric calculations:

FullSimplify[d > 0]
(*
Derivative[0, 1][StieltjesGamma][1, 1] > 0
*)

FullSimplify[Floor[d]]
(*
Floor[Derivative[0, 1][StieltjesGamma][1, 1]]
*)


There is a workaround: apply N directly to the second argument:

MapAt[N[#, 10] &, d, 2]
(*
0.70738627
*)


but, of course, it becomes cumbersome when a derivative occurs deeply nested in a more complex expression.

I understand that NHoldFirst is formally attached to the symbol StieltjesGamma itself, not to a non-symbol head Derivative[0, 1][StieltjesGamma]. But the behavior resulting from this seems wrong. Should it be considered as a bug that needs to be reported to Mathematica Tech Support, or there is a valid reason why NHoldFirst should not be propagated to derivatives? Do you know any simpler workarounds for this problem?

-
Very good question. Arguably you've found a corner case that's not properly handled (i.e., a bug). – Oleksandr R. Jun 24 '13 at 20:08
This works: N[Derivative[0, 1][StieltjesGamma][0, 1], 50], that is if the first argument is zero, but not apparently for positive integer arguments. Odd. – Michael E2 Jun 24 '13 at 22:44
@MichaelE2 That is probably because N[0, 50] evaluates to the exact integer 0. – Vladimir Reshetnikov Jun 24 '13 at 23:27
@VladimirReshetnikov Yes, that would explain it. Interesting that 0 is treated as exact and 1 is not. – Michael E2 Jun 25 '13 at 1:28

This is only a hack, but maybe it just gives you short way out of this. Lately, we had a similar discussion in chat about NValues where the problem was related. It this cases Rojo wanted to use NValues to prevent some of the arguments to stay untouched by N. There too, the problem was when N was called from very outside and dived into the subexpressions where you are unable to detect it. Maybe you want to read the chat-log of this conversation.

A simple way to achieve a back-conversion of a real valued first argument to StieltjesGamma define an additional rule for this function and inject some code:

Unprotect[StieltjesGamma];

StieltjesGamma[a_Real, b_] := Block[{$HaveSeenThisDefinition = True}, StieltjesGamma[Round[a], b] ] /; Not[TrueQ[$HaveSeenThisDefinition]];

Protect[StieltjesGamma];


When you now execute your examples (although you are still getting the warning messages) your result is computed correctly:

d = Derivative[0, 1][StieltjesGamma][1, 1];
N[d, 10]
(* 0.7073857926 *)


Side note, if you are not aware of the trick I used to inject code into a built-in function, see this answer for more details. Especially, the comment of Leonid explains quite clearly how it works.

-