# Evaluating function by replacing arguments

I have a function I want to evaluate by replacing its arguments with rule, however the result differs from when I manually enter the arguments,e.g.,

Given a functon

test[x_, y_, z_]:= Total@Log[DeleteCases[Chop[{x, y, z, 1 - x - y - z}], 0]]


Evaluation through applying rule yields undesirable answer:

In[2]:= test[x, y, z] /. {x -> 0.5622814903606034,
y -> 0.21885925481969834, z -> 0.21885925481969834}
Out[2]= -41.0444 + 3.14159 I


Manually entering the arguments gives the right answer:

In[3]:= test[0.5622814903606034, 0.21885925481969834, 0.21885925481969834]
Out[3]= -3.61441


However I don't have the time to enter the variables by hand every time, how do I make the replacing method work?

• define test as test[x_?NumericQ, y_?NumericQ, z_?NumericQ] := ... – kglr Jan 21 '18 at 10:32
• @kglr But this doesn't change the false result of calculating to complex number through replace? – updraft Jan 21 '18 at 10:40
• checkTrace[Log[DeleteCases[Chop[{x, y, z, 1 - x - y - z}], 0]] /. {x -> 0.5622814903606034, y -> 0.21885925481969834, z -> 0.21885925481969834}] to see why we get -41.0444 + 3.14159 I – kglr Jan 21 '18 at 10:44
• @kglr I believe fault lies in that the Chop and DeleteCases didn't kick into action as C.E. has alluded to since ReplaceAll doesn't have the HoldAll attribute. – updraft Jan 21 '18 at 10:54

I'll expand on kglr's comments. Consider what happens if you evaluate the function with x, y, z as its arguments:

test[x, y, z]


Log[x] + Log[y] + Log[1 - x - y - z] + Log[z]

You get this expression back. Log and Total are still reflected in this, but DeleteCases and Chop are not there. Now we evaluate this function with numeric arguments:

test2[x_, y_, z_] := Total@Log[{x, y, z, 1 - x - y - z}]
test2[0.5622814903606034, 0.21885925481969834, 0.21885925481969834]


-41.0444 + 3.14159 I

This is the same answer you got from

test[0.5622814903606034, 0.21885925481969834, 0.21885925481969834]


Now, why do you get this when you use /. (ReplaceAll)? The reason for this is that arguments of functions (in the absence of a HoldFirst/HoldAll attribute) evaluate before the function itself.

ReplaceAll[
test[x, y, z],
{x -> 0.5622814903606034, y -> 0.21885925481969834, z -> 0.21885925481969834}
]


evaluates first test[x,y,z], then {x -> 0.56..., y -> 0.21..., z -> 0.21...}, and then ReplaceAll[evaluated arg1, evaluated arg2].

In other words, your ReplaceAll code starts by simplifying to:

ReplaceAll[
Log[x] + Log[y] + Log[1 - x - y - z] + Log[z],
{x -> 0.5622814903606034, y -> 0.21885925481969834, z -> 0.21885925481969834}
]


Consider using With instead, which does much the same thing as ReplaceAll, but has the HoldAll attribute and won't evaluate the function until after it has done the replacement:

With[
{x = 0.5622814903606034, y = 0.21885925481969834, z = 0.21885925481969834},
test[x, y, z]
]


-3.61441

Defining test as

Clear[test]
test[x_?NumericQ, y_?NumericQ, z_?NumericQ] :=
Total@Log[DeleteCases[Chop[{x, y, z, 1 - x - y - z}], 0]]


works because now test[x, y, z] won't evaluate:

test[x, y, z]


test[x, y, z]

Consequently, the evaluation will happen in the order that you expected because ReplaceAll will have to be evaluated before test can be.

• So ReplaceAll doesn't have the HoldAll property, part of the function didn't work, much thanks for pointing this out! – updraft Jan 21 '18 at 10:57
• You're welcome, I'm glad it helped. – C. E. Jan 21 '18 at 11:00

You need to Clear the old definition of test before applying kglr's solution.

\$Version

(* "11.2.0 for Mac OS X x86 (64-bit) (September 11, 2017)" *)

Clear[test]

test[x_, y_, z_] := Total@Log[DeleteCases[Chop[{x, y, z, 1 - x - y - z}], 0]]

test[x, y, z] /. {x -> 0.5622814903606034, y -> 0.21885925481969834,
z -> 0.21885925481969834}

(*  -41.0444 + 3.14159 I *)

test[x_?NumericQ, y_?NumericQ, z_?NumericQ] :=
Total@Log[DeleteCases[Chop[{x, y, z, 1 - x - y - z}], 0]]


Without clearing the old definition of test

test[x, y, z] /. {x -> 0.5622814903606034, y -> 0.21885925481969834,
z -> 0.21885925481969834}

(* -41.0444 + 3.14159 I *)


Now after using Clear

Clear[test]

test[x_?NumericQ, y_?NumericQ, z_?NumericQ] :=
Total@Log[DeleteCases[Chop[{x, y, z, 1 - x - y - z}], 0]]

test[x, y, z] /. {x -> 0.5622814903606034, y -> 0.21885925481969834,
z -> 0.21885925481969834}

(* -3.61441 *)

• So this works after all, but why though? The arguments are numerical to begin with? – updraft Jan 21 '18 at 15:49
• The restrictions on the arguments are to keep the function from evaluating until the arguments are numerical, i.e., until after the replacements are made so that the Chop and DeleteCases have a chance to work. – Bob Hanlon Jan 21 '18 at 15:53
• Ah I forgot, sorry, thanks for the explanation. – updraft Jan 21 '18 at 15:55
• @L.Quen Did you read the end of my answer? I wrote about kglr's solution some hours before this answer was posted, although it was not present in my original answer so I'm not sure you read it... anyway, this answer spends more time on explaining how it works, +1. – C. E. Jan 21 '18 at 19:52

Providing an alternative to using With as suggested by C.E., in order to allow batch evaluation. This is done by simply adding HoldAll and then ReleaseHold:

ReleaseHold[Hold[test[x, y, z]] /. {{x -> x1, y -> y1, z -> z1}...}]