Skip to main content

list manipulation - 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?



Answer



Here is a solution for both "idioms", using LibraryLink and some kind of custom generics. I'm not sure if I like the abstractions, but here is the answer anyway.


<< SymbolicC`
<< Developer`

<< CCompilerDriver`
<< CCodeGenerator`

The definitions below are made to help abstract this function, so that it works for integers, real numbers and complex numbers.


(*lfn is short for library function name*)
(*LL is short for LibraryLink*)

typeTableHeaders = {"type name", "c type", "LL generic name",
"abbreviation in lfn"};
typeTable =

{
{Integer, "mint", "Integer", "I"},
{Real, "mreal", "Real", "R"},
{Complex, "mcomplex", "Complex", "C"}
};
cTypes = typeTable[[All, 2]];

abstractFunctionName = "parif";

libraryFunctionNames =

abstractFunctionName <> "T" <> # <> "_T" & /@ typeTable[[All, 4]];

getters = StringJoin["MArgument_get", #] & /@ typeTable[[All, 3]];

dataGetters = "MTensor_get" <> # <> "Data" & /@ typeTable[[All, 3]];

tensorTypes = "MType_" <> # & /@ typeTable[[All, 3]];

libraryFunctionNamesSingle = # <> "Single" & /@ libraryFunctionNames;
libraryFunctionNamesMulti = # <> "Multi" & /@ libraryFunctionNames;


relations =
Hold@
{
getter -> getters,
dataGetter -> dataGetters,
type -> cTypes ,
tensorType -> tensorTypes
};


preRules = Thread /@ First@ MapAt[HoldPattern, relations, {1, All, 1}];

The next definitions distinguish between the "single" case where we want to riffle with a constant, and the "multi" case where we want to riffle two lists. I have not taken the effort of making this code more expressive. That is, there is some duplicate code.


preRulesSingle =
Append[
preRules,
Thread[libraryFunctionName -> libraryFunctionNamesSingle]
];

singleDIERule =

setupSecondInputAccess :>
Sequence[
CDeclare[type, "inputSingleton"],
CAssign["inputSingleton", CCall[getter, CArray["Args", 1]]]
];

rulesSingle =
Thread[
Join[
preRulesSingle

,
{
HoldPattern@inputElement -> "inputSingleton",
HoldPattern@determineNextInputElement :> Sequence[],
disownIfNeeded :> Sequence[],
singleDIERule
}
]
];


preRulesMulti =
Append[
preRules,
Thread[libraryFunctionName -> libraryFunctionNamesMulti]
];


multiDIERule =
setupSecondInputAccess :>
Sequence[

CDeclare["MTensor", "input2"],
CAssign["input2",
CCall["MArgument_getMTensor", CArray["Args", 1]]],
CDeclare[CPointerType[type], "input2DataPtr"],
CAssign[
"input2DataPtr",
CCall[CPointerMember["libData", dataGetter], {"input2"}]
]
];


rulesMulti =
Thread[
Join[
preRulesMulti
,
{
HoldPattern@inputElement -> CDereference["input2DataPtr"],
HoldPattern@determineNextInputElement :>
COperator[Increment, "input2DataPtr"],
disownIfNeeded :>

CCall[CPointerMember["libData", "MTensor_disown"], "input2"],
multiDIERule
}
]
];

Now we construct some SymbolicC with some tokens inside it


parifSCHeld =
Hold@
CFunction[

"int"
,
libraryFunctionName
,
{{"WolframLibraryData", "libData"}, {"mint",
"Argc"}, {CPointerType["MArgument"], "Args"}, {"MArgument",
"Res"}}
,

CBlock[

{
CDeclare["int", CAssign["err", "LIBRARY_NO_ERROR"]],
CDeclare["MTensor", "input"],
CDeclare["MTensor", "result"],
CDeclare["int", "inputLength"],
CDeclare["mint", CArray["resultDimensions", 2]],
CAssign[
"input",
CCall["MArgument_getMTensor", CArray["Args", 0]]
],

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"],
{tensorType, 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["long", CAssign["iter", 0]]
,
setupSecondInputAccess
,
CWhile[
COperator[Less, {"iter", "inputLength"}],
CBlock[
{

CAssign[
CDereference["resultDataPtr"],
CDereference["inputDataPtr"]
],

COperator[Increment, "resultDataPtr"],
COperator[Increment, "inputDataPtr"],
CAssign[
CDereference["resultDataPtr"],
inputElement

],
determineNextInputElement,
COperator[Increment, "resultDataPtr"],
COperator[Increment, "iter"]
}
]
]
,
CCall["MArgument_setMTensor", {"Res", "result"}],
CCall[CPointerMember["libData", "MTensor_disown"], "input"],

disownIfNeeded,
CReturn["err"]
}
]
];

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


symbCWithReplacementsSingle = parifSCHeld //. rulesSingle;
symbCWithReplacementsMulti = parifSCHeld //. rulesMulti;
cStrings =

"DLLEXPORT" <> " " <> ToCCodeString@First@# & /@
Join[symbCWithReplacementsSingle, symbCWithReplacementsMulti];
cFunctionsString = StringRiffle[cStrings, "\n\n"];

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 <> cFunctionsString;

The code below creates the library.


libName = "parif";

CreateLibrary[totalCString, libName]

This loads the functions (again duplicate code)


parifFunctionsSingle = 
LibraryFunctionLoad[
libName, #2, {{#, 1, "Shared"}, {#}}, {#, 2}] & @@@
Transpose@{typeTable[[All, 1]], libraryFunctionNamesSingle};

Set @@ {ToExpression["parifSingle" <> #, InputForm,
Unevaluated], #2} & @@@

Transpose@{typeTable[[All, 4]], parifFunctionsSingle};

parifFunctionsMulti =
LibraryFunctionLoad[
libName, #2, {{#, 1, "Shared"}, {#, 1, "Shared"}}, {#, 2}] & @@@
Transpose@{typeTable[[All, 1]], libraryFunctionNamesMulti};

Set @@ {ToExpression["parifMulti" <> #, InputForm,
Unevaluated], #2} & @@@
Transpose@{typeTable[[All, 4]], parifFunctionsMulti};


Timing comparisons


betterish[a_List,const_]:=Transpose[{a,ConstantArray[const,Length@a]}];
arFlatWiz[a_,b_]:= ArrayFlatten[{{{a}\[Transpose],b}}];

nn=10^7;
randomIntegers=RandomInteger[100,nn];

(resB=betterish[randomIntegers,2])//RepeatedTiming//First
(resAFW=arFlatWiz[randomIntegers, 2])//RepeatedTiming//First

(resSI=parifSingleI[randomIntegers,2])//RepeatedTiming//First

resB === resAFW===resSI

gives



0.201
0.213
0.0769
True


And


randomReals1=RandomReal[1,nn];
randomReals2=RandomReal[1,nn];

(resT=Transpose@{randomReals1, randomReals2})//RepeatedTiming//First
(resMRLL=parifMultiR[randomReals1,randomReals2])//RepeatedTiming//First

resMRLL===resT


gives



0.18
0.0838
True

So indeed we are faster than Transpose here.


Comments

Popular posts from this blog

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...