Before beginning, I want to note that there are some similarities with the questions
Combining two lists of different dimensions into a list of all combinations of points?
How can I make threading more flexible?
but I have one more complicating factor beyond that question. Instead of simply adding an offset to each point in a list, I would also like to track which offsets are taken to generate new points, perhaps in the form of a string.
Desired Behavior
Beginning with a list of offsets and some list of points and strings that has been generated thus far
offsets={{0,0, "a"}, {1,0, "b"}, {0,1,"c"}};
list={{1,0, "b"}, {2,3, "bbcc"}};
I would like to create a function addOffsets
that would return the following
{{1,0,"ba"}, {2,0,"bb"}, {1,1, "bc"}, {2,3,"bbcca"},{3,3,"bbccb"},{2,4,"bbccc"}}
Notice that the letter for each offset has been added to the end of the current string. Again, I am not devoted to using a string to record this information--If you have an alternative data structure that would be more efficient, I am all game! I primarily care about recovering parents/children (and by extension recovering entire such chains) in my application.
What I have so far
addOneOffset[{x_, y_, string_}] :=
Table[{x + offsets[[i, 1]], y + offsets[[i, 2]],
string <> offsets[[i, 3]]}, {i, 1, Length[offsets]}]
addOffset[list_] := Flatten[Map[addOneOffset, list], 1]
Note that this produces the desired output. However, it feels clunky to me. In particular, it seems like there shouldn't be a need for a sub-function. Additionally, while I appreciate what Flatten[...,1]
is doing, I almost wish that such a command wasn't necessary. In my mind, it seems like I'm using the wrong Map
/Thread
/...
that is producing the wrong list structure.
The Questions
- Is this a reasonable implementation? Can anyone see a way to code this more cleanly? Are my mentioned concerns with my implementation valid? I.e., would Mathematica evaluate this more efficiently if it was written as a single function? Or does the kernel not really care?
- I am mainly interested in running commands like
Nest[addOffset,list, 12]
and even larger amounts of nesting. On my computer (Intel Core i7-6700k @ 4 GHz (4 physical cores, 8 logical cores), 20GB RAM, ... ) this command takes $3.90625 s$. My naive approach of altering theaddOffset
function to haveParallelMap
instead ofMap
actually increases the necessary time to $15.2344s$. Is this a process that is reasonable to attempt parallelization? If so, what would be a better way to go about this? - What about CUDA / OpenGL / ... implementation? Currently have a Nvidia GeForce GTX 750 Ti and would even potentially consider upgrading if suggested. Is this worth looking into? I admittedly know very little about this (Math grad student, not CS) so I can fully appreciate RTFM/STFW responses; guidelines / suggestions about whether this is even worth trying are appreciated before I devote loads of time to this avenue.
If it matters, I am currently running version 11.0.1.0
Edit
Per @wxffles suggestion (which I rather like, very clean!) I made the new function
wxfflesOffset[input_] :=
Flatten[Outer[{#1[[1]] + #2[[1]], #1[[2]] + #2[[2]], #1[[3]] <>#2[[3]]} &, input, offsets, 1], 1]
Running Nest[wxfflesOffset,list,12]
takes $5.25s$, which surprised me. My guess would have been that Outer
would have been faster than my version of using Map
to apply a user defined function (which has another Table
command inside). Perhaps there's a better way to nest @wxffles use of Outer
?
Outer[{#1[[1]] + #2[[1]], #1[[2]] + #2[[2]], #1[[3]] <> #2[[3]]} &, list, offsets, 1]
? $\endgroup$Nest
. Perhaps there's a better option? $\endgroup$