3
$\begingroup$

It looks like the very young feature of pattern matching (/replacing) within Associations (https://mathematica.stackexchange.com/a/119542/6804), with or without KeyValuePattern, still has quite some flaws.

Let's define

g[KeyValuePattern["x" -> x_?NumericQ]] := x^2;

This is ok:

g[<|"x" -> x[1]|>]
g[<|"x" -> x[1]|>] /. x[1] -> 0
(*==>*)
g[<|"x" -> x[1]|>]
0

But using g with Associations in numerical functions does not work:

FindMinimum[g@<|"x" -> x[1]|>, {{x[1], 0}}]
NIntegrate[g@<|"x" -> x[1]|>, {x[1], 0, 1}]
(*==>*)
FindMinimum::nrnum: The function value g[<|x->x[1]|>] is not a real number at {x[1]} = {0.}. >>
NIntegrate::inumr: The integrand g[<|x->x[1]|>] has evaluated to non-numerical values for all sampling points in the region with boundaries {{0,1}}. >>

Using Lists of rules, whether right away or via Normal works:

NIntegrate[g@Normal@<|"x" -> x[1]|>, {x[1], 0, 1}]
NIntegrate[g@{"x" -> x[1]}, {x[1], 0, 1}]
FindMinimum[g@{"x" -> x[1]}, {{x[1], 0}}]
FindMinimum[g@Normal@<|"x" -> x[1]|>, {{x[1], 0}}]
(*==>*)
0.333333
{0., {x[1] -> 0.}}

Am I missing something obvious or can this be considered a bug?

These things are quite important to have fixed if we want to use Associations for some object-oriented programming...


Edit

Thanks to Alexey Popkov I now know that everything I described here behaves as is to be expected. Since Association has HoldAllComplete, the expression g[<|"x" -> x[1]|>], created when x[1] is undefined, will always stay g[<|"x" -> x[1]|>], even when x[1] is assigned some value later.

I find this a bit confusing, Associations really don't behave like a markup-style Head that you might define yourself:

A plain

person["age" -> x, "height" -> 2]

does in general not behave like

Association["age" -> x, "size" -> 2]

You might even argue that the first variant is more readable for object-oriented programming, because the head gives information about the type of object.

$\endgroup$
1

1 Answer 1

7
$\begingroup$

Update

The original answer gives consistent explanation of this behavior as being a consequence of the HoldAllComplete attribute of Association. But before Mathematica 10.4 Association didn't have this attribute but evaluated in the same way. Here are outputs from version 10.0.1:

Association // Attributes
{Protected}
Association[x + x -> 1 + 1]
<|2 x -> 2|>
assoc = Association["x" -> y]
<|"x" -> y|>
y = 1; assoc
<|"x" -> y|>

This means that my original explanation is only superficially correct and the observed behavior comes not from the HoldAllComplete attribute of Association but from the fact that after the first evaluation of the syntactic construct "Association[...]" (where Association is indeed just an ordinary head like List or Hold) we get entirely different object whose behavior can't be explained simply considering attributes and which isn't like a usual container like List or Hold. But the behavior of this object upon evaluation in the standard situations is as if it would be a usual container like HoldComplete.


Original answer

What happens here is that Association has attribute HoldAllComplete and hence does not evaluate x[1] to a numerical value:

Clear[x];
assoc = <|"x" -> x[1]|>;
x[1] = 0;
assoc
<|"x" -> x[1]|>

Numerical functions work by assigning values to the variables and then evaluating the objective function, they don't perform replacement like in your example

g[<|"x" -> x[1]|>] /. x[1] -> 0
0

This is the reason why your approach fails, no bugs here.

$\endgroup$
13
  • $\begingroup$ Oh, right, all of these functions localize variables effectively using Block, so in a sense they overwrite global variables during each evaluation and then reset them again. $\endgroup$
    – masterxilo
    Jun 29, 2016 at 9:54
  • $\begingroup$ Just curious: Can you maybe elaborate on what you can read from the error message other than that Experimental'NumericalFunction is involved, and that the expression is converted to it before stuff starts failing. $\endgroup$
    – masterxilo
    Jun 29, 2016 at 9:59
  • $\begingroup$ I'm not sure whether your 'Further elaboration' really shows an additional problem. In line with what you said, Association has HoldAllComplete, so g[<|"x" -> x|>] will, after having been evaluated once, never evaluate to g[<|"x" -> 0|>], even when you assign 0 to x later on. I think the only real problem is in the inconsistency of Plot and NIntegrate et. al then? $\endgroup$
    – masterxilo
    Jun 29, 2016 at 22:10
  • $\begingroup$ @masterxilo It seems I was wrong saying that g[a_Association] /; NumericQ[a["x"]] := a["x"]^2; should work. But the problem is subtle: once evaluated assoc = <|"x" -> x|> will never become <|"x" -> 0|> BUT assoc["x"] will return 0 if x = 0. With my definition I tried to use this feature. Now I see that if the RHS of the expression y = g[<|"x" -> x|>] is evaluated once to g[<|"x" -> x|>], then x = 0; y will return g[<|"x" -> x|>] with my definition. $\endgroup$ Jun 29, 2016 at 22:26
  • $\begingroup$ @masterxilo It is indeed related to the HoldAllComplete attribute and works the same way if we use HoldComplete instead of Association. Try the definition h[HoldComplete[x_]] /; NumericQ[x] := x^2: evaluation of x =.; y = h[HoldComplete[x]]; x = 0; y returns h[HoldComplete[x]]. But if we replace HoldComplete with Hold, it returns 0. So I was wrong, I'll remove the corresponding part of my answer (but it will be kept in the edit history if one is interested). $\endgroup$ Jun 29, 2016 at 22:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.