12
$\begingroup$

How can I effectively extract and replace a rectangular subpart of a toroidal matrix, i.e. one where boundaries are connected at opposite ends? Since the submatrix can overhang the edges, a simple Take won't work, as the submatrix cannot be extracted if it is not continuous. At present, I construct a table of positions that are extracted/replaced via ReplacePart, but I feel there must be more elegant and faster solutions. Such answers could add to the usefulness of Elegant operations on matrix rows and columns. Consider the following example:

n = 8; (* matrix edge length *)
m = 4; (* submatrix edge length *)

matrix = Table[0, {n}, {n}];
sub = RandomReal[{.2, 1}, {m, m}]; (* random submatrix *)
pos = {6, 7}; (* position of sub assuming bottom left corner as origo *)

subPos = Table[{i, j}, {i, m}, {j, m}];
repPos = Map[Mod[# + pos - 1, n, 1] &, subPos, {2}]; (* list of positions to extract *)

new = ReplacePart[ReplacePart[matrix, Flatten@MapThread[Rule, {repPos, sub}, 2]], pos -> 2];
  (* Replace the explicit list of positions with the elements of sub *)

ArrayPlot[Reverse@Transpose@new, 
 ColorRules -> {2 -> Pink, 0 -> White}, Mesh -> All, ImageSize -> 300]

Mathematica graphics

Pink cell indicates the origin of the submatrix.

Question: Can the extraction/replacement done faster, assuming that matrix and sub could be huge (e.g. 10 000x10 000 and 50x50, respectively)?

$\endgroup$
1
  • 1
    $\begingroup$ +1 Excellently presented question. I wish we had many more of those. $\endgroup$
    – halirutan
    Commented Sep 25, 2012 at 2:07

1 Answer 1

11
$\begingroup$
 n = 8; m = 4;
 origin ={6,7};
 matrix = Table[0, {n}, {n}];
 sub2 = Array[s, {m, m}];
 sub3 = RandomReal[{.2, 1}, {m, m}];
 sub3[[1,1]]=2;


{dimmat, dimsub} = Dimensions /@ {matrix, sub2};
(* using Istvan's suggestion for generating row and column indices of the part to be replaced:*)
{rows, columns} =  Mod[Range @@@ Transpose@{origin, origin + dimsub - 1}, dimmat, 1];
matrix[[rows, columns]] = sub2;
matrix // Grid

enter image description here

matrix[[rows, columns]] = sub3;
matrix // Grid

enter image description here

ArrayPlot[Reverse@Transpose@matrix, 
     ColorRules -> {2 -> Pink, 0 -> White}, Mesh -> All, ImageSize -> 300]

enter image description here

Note: Finding the row and column indices takes almost no time. For random 10,000x10,000 matrixs and random 50 by 50 submatrices, with random origins in the last 100x100 block, it takes less than a second to replace the parts of the matrix.

$\endgroup$
2
  • 1
    $\begingroup$ Thanks, I always forget about this way of part extraction and reassignment! However, I still would prefer the modulo: {rows, columns} = Mod[Range @@@ Transpose@{origin, origin + m - 1}, n, 1]. $\endgroup$ Commented Sep 24, 2012 at 13:55
  • $\begingroup$ Istvan, thanks for the accept. I also prefer your Mod[...]. $\endgroup$
    – kglr
    Commented Sep 24, 2012 at 22:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.