# Riffle and Partition

At some point I noticed that I was using Riffle and Partition together a lot. I would do things like

Partition[Riffle[{1,2,3},{4,5,6}],2]


or

Partition[Riffle[{1,2,3}, 6 , {2, -1, 2}], 2]


The question is: are better alternatives, in terms of memory and speed?

-
{{1, 2, 3}, {4, 5, 6}}\[Transpose]? 3x speedup. Thread[{{1, 2, 3}, 6}] 6x –  Kuba Apr 23 at 18:55
@Kuba yup ;). See also my chat with Silvia. Anyway I was focussing on the LibraryLink solution for the second "idiom" first. Also see the functions betterish at the bottom of my answer. –  Jacob Akkerboom Apr 23 at 18:57

Here is a solution for the second "idiom", using LibraryLink, that works only with integer packedarrays.

<< SymbolicC
<< Developer
<< CCompilerDriver
<< CCodeGenerator


The definitions below are made to help abstract this function, so that we can also make it work for real/complex numbers.

type = "mint";

abstractFunctionName = "parif";

abstractFunctionNamer[type_String] := StringJoin["parifT", type, "_T"];

mainFunctionName = abstractFunctionNamer["I"]

argumentSingletonGetterFunctionName[type_String] :=
StringJoin["MArgument_get", type];

getter = argumentSingletonGetterFunctionName["Integer"]

dataGetterAbstractor[type_String] :=
"MTensor_get" <> type <> "Data"

dataGetter = dataGetterAbstractor["Integer"]

typeSpecWL = "MType_Integer";


First let's generate some SymbolicC

parifSC =
CFunction[
type
,
mainFunctionName
,
{{"WolframLibraryData", "libData"}, {"mint",
"Argc"}, {CPointerType["MArgument"], "Args"}, {"MArgument",
"Res"}}
,

CBlock[
{
CDeclare["int", CAssign["err", "LIBRARY_NO_ERROR"]],
CDeclare["MTensor", "input"],
CDeclare[type, "inputSingleton"],
CDeclare["MTensor", "result"],
CDeclare[type, "inputLength"],
CDeclare[type, CArray["resultDimensions", 2]],
CAssign[
"input",
CCall["MArgument_getMTensor", CArray["Args", 0]]
],
CAssign[
"inputSingleton",
CCall[getter, CArray["Args", 1]]
],
CAssign[
"inputLength",
CDereference[
CCall[
CPointerMember["libData", "MTensor_getDimensions"],
{"input"}
]
]
],
CAssign[
CArray["resultDimensions", 0],
"inputLength"
],
CAssign[
CArray["resultDimensions", 1],
2
]
,
CAssign["err",
CCall[
CPointerMember["libData", "MTensor_new"],
]
]
,
CDeclare[CPointerType[type], "inputDataPtr"],
CAssign[
"inputDataPtr",
CCall[
CPointerMember["libData", dataGetter],
{"input"}
]
],
CDeclare[CPointerType[type], "resultDataPtr"],
CAssign[
"resultDataPtr",
CCall[
CPointerMember["libData", dataGetter],
{"result"}
]
]
,
CDeclare[type, CAssign["iter", 0]]
,
CWhile[
COperator[Less, {"iter", "inputLength"}],
CBlock[
{
CAssign[
CDereference["resultDataPtr"],
CDereference["inputDataPtr"]
],

COperator[Increment, "resultDataPtr"],
COperator[Increment, "inputDataPtr"],
CAssign[
CDereference["resultDataPtr"],
"inputSingleton"
],
COperator[Increment, "resultDataPtr"],
COperator[Increment, "iter"]
}
]
]
,
CCall["MArgument_setMTensor", {"Res", "result"}],
CCall[CPointerMember["libData", "MTensor_disown"], "input"],
CReturn["err"]
}
]
];


Now, let's turn it into a string, that would normally correspond to the contents of a .c file.

cCodeString = "DLLEXPORT"<> " " <> ToCCodeString[parifSC];

boilerPlate = "
#include \"WolframLibrary.h\"

/* Return the version of Library Link */
DLLEXPORT mint WolframLibrary_getVersion( ) {
\treturn WolframLibraryVersion;
}

/* Initialize Library */
DLLEXPORT int WolframLibrary_initialize( WolframLibraryData \
libData) {
\treturn LIBRARY_NO_ERROR;
}

/* Uninitialize Library */
DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData \
libData) {
\treturn;
}

";

totalCString = boilerPlate <> cCodeString;


The code below creates the library. I have added a counter, so that if you change the code you can be sure a new library is generated.

If[! ValueQ[counter], counter = 1;];
counter++;
counterString = ToString[counter];
libraryName = abstractFunctionName <> "Lib" <> counterString;
lib = CreateLibrary[totalCString, libraryName];


LibraryLoad[libraryName]

parifLL =
mainFunctionName, {{Integer, 1, "Shared"}, {Integer}}, {Integer, 2}]


Timing comparisons

nn = 1*^7; ;
mm = 10;

a = RandomInteger[100, nn];

betterish[a_List, const_] :=
Transpose[{a, ConstantArray[const, Length@a]}]
worseish[a_, const_] := Partition[Riffle[a, const , {2, -1, 2}], 2]

nn = 5*10^6;
mm = 10;
timeTotal = Function[Null, Do[#, {mm}] // Timing // First, HoldFirst];

timeTotal[resW = worseish[a, 1]]
timeTotal[resB = betterish[a, 1]]
timeTotal[resLL = parifLL[a, 1]]


gives

20.453187
3.392331
2.131926
0.904720

-
@Kuba but there is Function written out ;). Ah by the way for a big moment I was quite scared Thread would shred this answer :P, but its not so nice. Hmm.. I made very nice large code blocks that are easy to run, sure you don't wanna simply run it without understanding it :P? It always makes me nervous to suggest people to use OpenWrite etc though. –  Jacob Akkerboom Apr 23 at 19:09
I don't like running the code I don't understand, unless I want to get it :) –  Kuba Apr 23 at 19:11
@Kuba but don't you know how many little computer gnome calculator slaves are killed every time you run Riffle :P? You run that without knowing how it is implemented, don't you ;)? –  Jacob Akkerboom Apr 23 at 19:14
This kind of logic will lead us to Assembler, won't it? :) –  Kuba Apr 23 at 19:16
@Kuba ain't nobody got time for that, I have to hunt for my own food! –  Jacob Akkerboom Apr 23 at 19:16

Kind of stolen from Kuba's earlier comment:

Another way to rewrite the original expression if speed is not a huge concern is via Thread Transpose as in:

Transpose[{{1, 2, 3}, {4, 5, 6}}]


which is especially pleasing to the eye if your initial lists are named variables (Transpose@{a, b}). On my machine this variant seems to be 10 times slower than the original Riffle and Partition.

The advantage and reason for posting (besides being easier to read) is that this construct can be used to merge more than two lists which is especially handy for e.g. ListContourPlot

ListContourPlot@Transpose[{xValues, yValues, fValues}]

-

Since you only give two examples of your use of Riffle and Partition, and since I don't make common use of this combination myself, I can only address those specific examples.

The first can be done nearly an order of magnitude faster on packed arrays with a naive application of Transpose (as already mentioned by Kuba):

a = Range[3*^6];
b = Mod[a, 10];
Partition[Riffle[a, b], 2]  // timeAvg
{a, b}\[Transpose]          // timeAvg

0.03928

0.005616


(Search the site for timeAvg code; I have posted it many times.)

The second can be done several times faster with ArrayFlatten:

a = Range[3*^6]; b = 6;

Partition[Riffle[a, b, {2, -1, 2}], 2] // timeAvg
ArrayFlatten[{{{a}\[Transpose], b}}]   // timeAvg

0.04992

0.01684


For other methods and timings see this closely related Q&A: Prepend 0 to sublists

-