7
$\begingroup$

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.

$\endgroup$
4
  • $\begingroup$ 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`"] $\endgroup$
    – ssch
    Feb 19, 2013 at 0:33
  • $\begingroup$ @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? $\endgroup$
    – Szabolcs
    Feb 19, 2013 at 0:36
  • $\begingroup$ 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? $\endgroup$
    – Rojo
    Feb 19, 2013 at 4:21
  • $\begingroup$ @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. $\endgroup$
    – Szabolcs
    Feb 19, 2013 at 4:25

2 Answers 2

11
$\begingroup$

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.

$\endgroup$
7
  • 2
    $\begingroup$ 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. $\endgroup$ Feb 19, 2013 at 22:12
  • $\begingroup$ @LeonidShifrin very good point! I added it. $\endgroup$ Feb 19, 2013 at 22:23
  • $\begingroup$ @OleksandrR. A nice use of Return - this version did not occur to me here. $\endgroup$ Feb 19, 2013 at 22:42
  • $\begingroup$ I see that you are using And for flow control as Simon Woods did here. Is that something you often do? $\endgroup$
    – Mr.Wizard
    Feb 20, 2013 at 0:19
  • $\begingroup$ @Mr.Wizard not usually, but I'm not averse to it either. It seemed within the spirit of a Condition. $\endgroup$ Feb 20, 2013 at 0:26
9
$\begingroup$

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 *)
$\endgroup$

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.