Consider this example:
array = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
array // Developer`PackedArrayQ
(* False *)
packedarray = Developer`ToPackedArray[array];
packedarray // Developer`PackedArrayQ
(* True *)
arrayCS = Internal`CopyListStructure[array, Flatten@array];
arrayCS // Developer`PackedArrayQ
(* False *)
packedarrayCS =
Internal`CopyListStructure[packedarray, Flatten@packedarray];
packedarrayCS // Developer`PackedArrayQ
(* False *)
So, my question is: Is there an option to make Internal`CopyListStructure
to output packed array given packed array data as input, so that one doesn't have to do one more Developer`ToPackedArray
over the output?
Update
Although my original question stands up there as it is, here'st clarification per comments why I Map
a RuntimeAttributes -> {Listable}
function over the dataset.
My actual input is not a rectangular array, so I don't use ArrayReshape
for the same reason.
Building on example by @b3m2a1 - here's the way it works in case the input data is of rectangular shape:
makeGrid[t1_, t2_, t3_, t4_, t5_, cf_, cfMap_, ar_, unfl_, ve_] :=
Module[{data, outputOK, packedArrayQ},
outputOK = SameQ[cfMap, #] & /@ {cf, cfMap, ar, unfl, ve};
packedArrayQ = Developer`PackedArrayQ /@ {cf, cfMap, ar, unfl, ve};
data =
SortBy[Join[{{"Listable Function @", t1}, {"Listable Function /@",
t2}, {"Flatten>>ArrayReshape",
t3}, {"Flatten>>Internal`CopyListStructure",
t4}, {"Vectorized", t5}}, Partition[outputOK, 1],
Partition[packedArrayQ, 1], 2], #[[2]] &];
Text@Grid[
Prepend[data, {"Approach", "RepeatedTiming", "SameQ",
"PackedArrayQ"}],
Background -> {None, {Lighter[Yellow, .9], {White,
Lighter[Blend[{Blue, Green}], .8]}}},
Dividers -> {{Darker[Gray, .6], {Lighter[Gray, .5]},
Darker[Gray, .6]}, {Darker[Gray, .6],
Darker[Gray, .6], {False}, Darker[Gray, .6]}},
Alignment -> {{Left, Right, {Left}}}, ItemSize -> {{22, 10}},
Frame -> Darker[Gray, .6], ItemStyle -> 14,
Spacings -> {Automatic, .8}]
];
cf1 = Compile[{{x, _Real}}, x^2, RuntimeAttributes -> {Listable}];
rr3 = RandomReal[{}, {115, 222, 30}];
{t1, cf} = cf1[rr3] // RepeatedTiming;
{t2, cfMap} = cf1 /@ rr3 // RepeatedTiming;
{t3, blank} = RepeatedTiming[
flrr3 = Flatten@rr3;
drr3 = Dimensions@rr3;
ar = ArrayReshape[cf1[flrr3], drr3];
];
{t4, blank} = RepeatedTiming[
flrr3 = Flatten@rr3;
unfl = Internal`CopyListStructure[rr3, cf1[flrr3]];
];
{t5, ve} = rr3^2 // RepeatedTiming;
makeGrid[t1, t2, t3, t4, t5, cf, cfMap, ar, unfl, ve]
So, it holds true that vectorized approach is the fastest and so forth.
However, this is what it looks like in case the data is not rectangular-shaped:
rr3[[1, 1, 1]] = Nothing;
{t1, cf} = cf1[rr3] // RepeatedTiming;
{t2, cfMap} = cf1 /@ rr3 // RepeatedTiming;
{t3, blank} = RepeatedTiming[
flrr3 = Flatten@rr3;
drr3 = Dimensions@rr3;
ar = ArrayReshape[cf1[flrr3], drr3];
];
{t4, blank} = RepeatedTiming[
flrr3 = Flatten@rr3;
unfl = Internal`CopyListStructure[rr3, cf1[flrr3]];
];
{t5, ve} = rr3^2 // RepeatedTiming;
makeGrid[t1, t2, t3, t4, t5, cf, cfMap, ar, unfl, ve]
Part
to assignNothing
> unflatten back. $\endgroup$f[nestedList]
, iff
isListable
? If you're going to be applying a listable function to the bottom level, it might be good to leave things unpacked (f[packed]
yields unpacked, ifListable
). [Your comment makes an example use-case seem important.] $\endgroup$Listable
function is ultimately applied only to non-List
elements at the bottom levels of a nested list and not to the sublists. By "ultimately applied," I mean the function is evaluated.F[{a, {b}}]
becomes{F[a], {F[b]}}
before any definition-rules forF[]
are used. Can you give a similar concrete example where my claims seem to be wrong? $\endgroup$#^2 &
is rather a special choice for a test function. (2) In the OP, the originalarray
was not packed; does the data start in packed or unpacked form? You should including the time to pack it, if not. Vectorized loses some of its advantage then. (3) You should use (or try) the optionParallelization -> True
incf1
. $\endgroup$