27

Bug introduced in 10.4 and fixed in 11.3.0


I create two associations, that are supposed to be exactly the same. And then I want to count the elements at the second level. And I get different results:

x = Range[2];
a1 = <|"a" -> x|>;
a2 = <|"a" -> {1, 2}|>;
a1
a2
a1 === a2
Count[a1, _, {2}]
Count[a2, _, {2}]

<|"a" -> {1, 2}|>

<|"a" -> {1, 2}|>

True

0

2

It just doesn't make sense to me. What is going on?

ilian
  • 25,474
  • 4
  • 117
  • 186
Stitch
  • 4,205
  • 1
  • 12
  • 28

3 Answers3

22

As Kirill Belov notes in a comment, the issue is related to the fact that the list a1 is a packed array (generated by Range) whereas the list a2 is not packed. Count, Position and Depth unexpectedly act as if the packed array is atomic. This is very likely a bug since 1) the expected behaviour occurs if the top-level expression is a list instead of an association and 2) many other level-sensitive functions yield the expected results.

Analysis (current as of version 11.1)

For discussion purposes, let us consider the following two associations:

packed =   <| "a" -> Developer`ToPackedArray[{1, 2}] |>;
unpacked = <| "a" -> Developer`FromPackedArray[{1, 2}] |>;

We will apply various operators to these values:

operators and values

The results show that Count, Position and Depth act as if the packed array were atomic. The results for these operations can be explained by the TreeForm structure diagrams shown in the table if we consider all of the internal association structural details to be a "single level" (i.e. the AssociationNodes from Assocation down to Rule).

On the other hand, these results are not consistent with those operations that appear in the table below the structure diagrams. Cases, Level, Total, Map and Replace all treat the packed array as if it were not atomic.

Furthermore, even Count, Position and Depth stop treating the packed array as atomic if the top-level expression is a list instead of an association:

operators and values

We can see from this second table the results are all consistent for the various level-sensitive operators -- except when Count, Position and Depth acting upon a packed array contained within an association. This is almost certainly a bug.

WReach
  • 68,832
  • 4
  • 164
  • 269
1

I don't know why, but this works:

x = Range[2]; 
a1 = ToExpression[ Association["a" -> x]]; 
a2 = Association["a" -> {1, 2}]; 
a1
a2
a1 === a2
Count[a1, _, {2}]
Count[a2, _, {2}]
Ryhor
  • 51
  • 2
-4

It really depends on how to define an evaluation. For instance, the Association is <|"a" -> "PackedArray"[Integer, "<" 2 ">"]|>, Some people think it's evaluated and this is the irreducible value, I think it's not evaluated or a special form. No matter you know it's a PackedArray or not, if you treat x as an unevaluated symbol, it would be not a surprise to see such a result. Nevertheless, It's good to know it's a PackedArray.

There are some examples which can prove x3 = Range[2] is not "evaluated"/ in a special form.

This issue is a bit tricky, it's related to Range function, if you use x3 = Table[i, {i, 2}], you will not have this issue.

CountAssociation[x_, level_] := 
 Count[Association["a" -> x], _, {level}]

x1 = {{1, 2}, {1, 2}, {1, 2}}
x3 = Range[2]

CountAssociation[x1, 2] (*3*)
CountAssociation[x1, 3] (*6*)

CountAssociation[{x3}, 2] (*1*)
CountAssociation[{x3, 
  x3}, 2] (*2*)
CountAssociation[{x3, x3, 
  x3}, 2](*3*)
CountAssociation[x3, 2](*0*)
CountAssociation[{x3}, 3](*0*)

In:

Remove[a1, x1, x2] 
x1 = Range[2];
x2 = {1, 2};
CountAssociation[x_] := Count[Association["a" -> x], _, {2}]
CountAssociation[x1]
CountAssociation[x2]

Out:

0
2

Both of x1 and x2 are not stateful computation, Use x1 and x2 as inputs to CountAssociation respectively, the outputs are different.

CountAssociation has not side effect, in other words, CountAssociation's input decides the output, the same inputs always output the same result. (Assuming the Inputs are not stateful computation)

It means x1 and x2 are not the same input.

Why? It's about lazy evaluation and force evaluation. Please check the code below.

When you print x in Mathematica, x is evaluated, so you can see the evaluated result of x. However x isn't evaluated when you use x to construct an association or assign x as a value of an association.

x is not evaluated, x is symbol but not a list to Count, that's why the result is 0 but not 2.

To solve this issue you have to force evaluate x to a list, you can use Map[Identity].

In:

Remove[a1, y]
y
a1 = <|"a" -> y|>;
Count[a1, _, {2}]

Remove[a1, x]
x = Range[2] // Map[Identity]
a1 = <|"a" -> x|>;
Count[a1, _, {2}]

Remove[x, a1]
x = Range[2];
a1 = <||>;
a1["a"] = x // Map[Identity]
Count[a1, _, {2}]

Out:

y
0

{1, 2}
2

{1, 2}
2
webcpu
  • 3,182
  • 12
  • 17
  • 2
    Check x = Range[2]; a1 = <|"a" -> x|>; OwnValues[a1], the output is {HoldPattern[a1] :> <|"a" -> {1, 2}|>}. Hence x is evaluated on creation of the association. – Alexey Popkov Apr 29 '17 at 09:07
  • Related: https://mathematica.stackexchange.com/a/119607/280 – Alexey Popkov Apr 29 '17 at 09:12
  • As I mentioned before, x is not evaluated, otherwise, x should be as same as {1,2}, but it's not true if x is not evaluated. – webcpu Apr 29 '17 at 09:24
  • You are completely wrong: check OwnValues as I wrote above! The only (hidden) difference is in packing, see this comment. – Alexey Popkov Apr 29 '17 at 09:26
  • Map[Identity] in your code simply unpacks the list, it doesn't affect evaluation of x. – Alexey Popkov Apr 29 '17 at 09:28
  • x = {1,2}; a1 = <|"a" -> x|>; OwnValues[a1], the output is {HoldPattern[a1] :> <|"a" -> {1, 2}|>}. Both of outputs are the same. However the result are different, how can you explain that? – webcpu Apr 29 '17 at 09:34
  • Map[Identity] is a sort of force evaluation in this case, so x can't be lazy evaluated. – webcpu Apr 29 '17 at 09:39
  • You completely misunderstand the working of Set which implies immediate evaluation of the r.h.s. The explanation is packing, see the comment I linked above. – Alexey Popkov Apr 29 '17 at 11:05
  • Evaluation has many levels, in this case, x is not force evaluated to irreducible form and x is kept intact. – webcpu Apr 29 '17 at 13:59
  • Output of a1["a"] = x is {1, 2} what proves that x is indeed evaluated. I do not understand why you are arguing against the obviousness. – Alexey Popkov Apr 29 '17 at 14:49
  • @AlexeyPopkov Alexey is correct. To confirm this, we can Remove the x symbol after assigning a1, so x is not needed neither it is used for any evaluation after the first Set. – Stitch Apr 29 '17 at 15:08
  • Function Range is a known special case. Function Table doesn't have this issue. – webcpu Apr 29 '17 at 15:35
  • "there are some examples which can prove x3 = Range[2] is not evaluated" -- Could you please provide such an example? – jjc385 Apr 29 '17 at 16:34
  • Yes, please check the last code snippet of my answer. – webcpu Apr 29 '17 at 17:11
  • @UnchartedWorks As others have mentioned, you have NOT demonstrated your claim. You've shown that something is different between Range[2] and {1,2}, but you have failed to demonstrate that the issue is evaluation. Indeed, others have convincingly argued the difference is in array packing, not evaluation. (Also, I didn't get notified because you didn't ping me with @.) – jjc385 Apr 30 '17 at 15:29
  • @jjc385 It really depends on how to define an evaluation. For instance, the Association is <|"a" -> "PackedArray"[Integer, "<" 2 ">"]|>, Some people think it's evaluated and this is the irreducible value, I think it's not evaluated or a special form. No matter I know it's a PackedArray or not, if you treat x is a unevaluated symbol, it would be not a surprise to see such a result. Nevertheless, It's good to know it's a PackedArray. BTW, I did demonstrate it in the last part of my answer. For instance, CountAssociation[x3, 2] (0) CountAssociation[{x3}, 3] (0) – webcpu Apr 30 '17 at 15:43
  • @UnchartedWorks I think if you edited your answer to clearly reflect this, people might remove their downvotes. I think the biggest problem here is you've used words like 'evaluation' which have specific (yet irritatingly opaque) meaning for Mathematica in perhaps a more colloquial way, and people misunderstood your meaning. I think clarifying this (and getting uber-experienced users' opinions on what's going on) would be very useful for clarifying my own understanding of the evaluation process. – jjc385 Apr 30 '17 at 15:50
  • @UnchartedWorks It's worth mentioning that this has been labeled as a bug (by the community), and my impression is that packing is intended to be invisible to the evaluation process. Caveat: I've never even heard of packing before reading this question, and I was shocked that FullForm doesn't give complete information about the state of e.g. Range[2]. Though I have read enough of e.g. Leonid's answers to have a vague sense that there's a lot going on under the hood that I don't understand. – jjc385 Apr 30 '17 at 15:53
  • @UnchartedWorks Last thing: You said "BTW, I did demonstrate it in the last part of my answer. For instance, CountAssociation[x3, 2] (0) CountAssociation[{x3}, 3] (0) ". To be clear, you've demonstrated that something is different. You haven't logically demonstrated that your claims about evaluation is correct. (I know the following isn't the case here, but for instance your examples don't preclude a bug in Count.) – jjc385 Apr 30 '17 at 15:56
  • @jjc385 I feel it's difficult to explain it. I haven't noticed that Function Evaluate doesn't really work as expected. So I use some tricks to Force Evaluate expression, such as Map[Identity]. Apart from this, there is another kind of Evaluation, Deep Evaluation, I haven't found the corresponding function for Deep Evaluation, so I don't have much to say about Deep Evaluation. Thanks for your advice. :) – webcpu Apr 30 '17 at 15:58
  • Comments are not for extended discussion; this conversation has been moved to chat. – Kuba Apr 30 '17 at 16:05