Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I am looking for the fastest way to test whether an n-dimensional numerical (NumericQ) array (in the ArrayQ sense) is complex or not. It is considered complex if it has at least one complex element.

It is an important requirement that this test must not unpack packed arrays.

Currently I have Not@ArrayQ[arr, _, FreeQ[#, _Complex] &], which does not unpack, but it executes the test for each element of packed arrays (can be tested using Print). Theoretically it's sufficient to test only one: all elements of a packed array are necessarily of the same type. In theory it's possible to detect whether an array is packed, and if it is, only test the first element, but this is very ugly, so I'd like to avoid it. It' also not easy to implement for an n-dimensional array.

What I am hoping for is a function which is internally special cased for packed arrays and is thus very fast.

share|improve this question
    
Something like PackedArrayQ[arr, Complex] || Not@ArrayQ[arr, _, FreeQ[#, _Complex] &] (I don't know how to check if PackedArrayQ unpacks and/or tests each element) Edit: Needs["Developer`"] –  ssch Feb 19 '13 at 0:33
    
@ssch I didn't know about the second argument of PackedArrayQ. Perhaps something like Deverloper`PackedArrayQ[arr, Complex] || (Not@Developer`PackedArrayQ[arr] && Not@FreeQ[arr, Complex]). Care to post an answer? –  Szabolcs Feb 19 '13 at 0:36
    
What exactly would be a compex array? One in which every element belongs to Complexes, no matter if they are real or integers, but at least one of them is actually a complex? Or any array with at least one Complex element? Or all complex elements? Your test seems to define it as an array in which at least one of its elements has a complex subelement. Is that the idea? –  Rojo Feb 19 '13 at 4:21
    
@Rojo Okay, clarifications: the array is guaranteed to be numeric (as in NumericQ). If it has at least one complex element, it is considered to be complex. This is why I had Not@ ... FreeQ instead of MemberQ. Use case: need to decide how to transfer the data through MathLink so it's easy to handle in C on the other side. –  Szabolcs Feb 19 '13 at 4:25

2 Answers 2

up vote 10 down vote accepted

One potential problem with directly testing the complete list using Developer`PackedArray[arr_, type_] is that this test may fail if the whole array is not packed, but sub-arrays are. As a result, one can too easily fall through to the more general test using FreeQ (or MemberQ) and end up unpacking anyway. This can be avoided by using ReplaceAll to remove packed arrays in advance of the membership test, which is a trick I also used here.

For present purposes, we might write:

ClearAll[containsComplexesQ];
containsComplexesQ[expr_] := MemberQ[
  expr /. {
     l_List /; Developer`PackedArrayQ[l, Complex] &&
                Return[True, containsComplexesQ] -> Null, 
     l_List /; Developer`PackedArrayQ[l] -> Null
    }, _Complex, -1
  ];

Now:

On["Packing"];
testArray = {RandomReal[1, 10], RandomComplex[1 + I, 10]};

containsComplexesQ[testArray]
(* -> True *)

An important difference between MemberQ and FreeQ is that the former has Heads -> False, while the latter has Heads -> True by default. Although one may of course specify Heads -> True to mimic the behavior of the example given in the question, since the intended application is to test array-like expressions, it's just as well to leave the default as it is. Furthermore, it's sufficient for the purpose of determining whether any complex values are present to test only level -1 of the structure, rather than every level as FreeQ does by default. As a result, a judicious use of MemberQ as above may perform somewhat better than Composition[Not, FreeQ] by virtue of testing fewer subexpressions.

share|improve this answer
2  
But why not exit e.g. through exception throwing upon the first case where Developer`PackedArrayQ[l, Complex] woul d give True? It should be generally faster than either of the options you mentioned. –  Leonid Shifrin Feb 19 '13 at 22:12
    
@LeonidShifrin very good point! I added it. –  Oleksandr R. Feb 19 '13 at 22:23
    
@OleksandrR. A nice use of Return - this version did not occur to me here. –  Leonid Shifrin Feb 19 '13 at 22:42
    
I see that you are using And for flow control as Simon Woods did here. Is that something you often do? –  Mr.Wizard Feb 20 '13 at 0:19
    
@Mr.Wizard not usually, but I'm not averse to it either. It seemed within the spirit of a Condition. –  Oleksandr R. Feb 20 '13 at 0:26

In the Developer Utilities Package there's a function PackedArrayQ that checks if an expression is a packed array, optionally of a certain type.

This can be combined with the previous test:

ComplexArrayQ[arr_]:=Developer`PackedArrayQ[arr, Complex] ||
           (Not@Developer`PackedArrayQ[arr] && Not@FreeQ[arr, Complex])

arr = RandomComplex[1 + I, {500, 10}];
Developer`PackedArrayQ[arr, Complex] (* True *)
arr[[3,1;;4]]=I;
Developer`PackedArrayQ[arr, Complex] (* False *)
ComplexArrayQ@arr                    (* True *)
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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