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.

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?

share|improve this question
4  
{{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

3 Answers 3

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"],
       {typeSpecWL, 2, "resultDimensions", CAddress["result"]}
       ]
      ]
     ,
     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];

This loads the function

LibraryLoad[libraryName]

parifLL = 
 LibraryFunctionLoad[libraryName, 
  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[resThread = Thread[{a, 1}]]
timeTotal[resW = worseish[a, 1]]
timeTotal[resB = betterish[a, 1]]
timeTotal[resLL = parifLL[a, 1]]

gives

20.453187  
3.392331  
2.131926  
0.904720
share|improve this answer
    
@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}]
share|improve this answer

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

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.