4
$\begingroup$

I am interested in efficient element-wise multiplication of matrices with different dimension. Here is my solution:

Matrix 1 with dim = {3, 4, 4}

mat1 = RandomInteger[10, {3, 4, 4}]
(*{{{6, 0, 0, 3}, {3, 7, 6, 4}, {0, 4, 9, 3}, {6, 9, 3, 0}}, {{1, 7, 1, 
   4}, {3, 7, 6, 2}, {3, 6, 1, 9}, {3, 6, 4, 1}}, {{1, 7, 1, 0}, {4, 
   3, 9, 2}, {9, 10, 1, 3}, {2, 3, 2, 8}}}*)

Matrix 2 (dimension {4, 4}):

mat2 = RandomInteger[10, {4, 4}]
(*{{9, 3, 8, 6}, {2, 0, 2, 5}, {9, 8, 5, 9}, {10, 5, 2, 10}}*)

To let each element in mat2 times with the element in the "subset matrix " of mat1, because the subset has same dimension i.e. {4, 4} in this case, so the element-wise multiplication is legal:

Result = Table[mat2 * mat1 [[i]], {i, 1, 3}]
(*{{{54, 0, 0, 18}, {6, 0, 12, 20}, {0, 32, 45, 27}, {60, 45, 6, 0}}, 
   {{9, 21, 8, 24}, {6, 0, 12, 10}, {27, 48, 5, 81}, {30, 30, 8, 10}},
   {{9, 21, 8, 0}, {8, 0, 18, 10}, {81, 80, 5, 27}, {20, 15, 4, 80}}}*)

but I don't know if there is a better way to do this, especially in terms of achieving better efficiency.

$\endgroup$
6
$\begingroup$

You may Compile the code into a listable CompiledFunction as follows:

cf = Compile[{{a, _Integer, 2}, {b, _Integer, 2}},
   a b,
   RuntimeAttributes -> {Listable},
   Parallelization -> True
   ];

Here is a comparison

n = 100000;
m = 16;
mat1 = RandomInteger[10, {n, m, m}];
mat2 = RandomInteger[10, {m, m}];


Result = Table[mat2*mat1[[i]], {i, 1, Length[mat1]}]; // RepeatedTiming // First
Result2 = cf[mat1, mat2]; // RepeatedTiming // First
Result == Result2

0.342

0.067

True

$\endgroup$
  • $\begingroup$ Thanks for the powerful answer! $\endgroup$ – leon365 Mar 20 at 23:03
  • 1
    $\begingroup$ You're welcome. $\endgroup$ – Henrik Schumacher Mar 20 at 23:04
  • $\begingroup$ This is genius. mat1 is a rank-three tensor, and this code relies on the rank-2 tensor pattern in the argument of cf binding more strongly than the listability of the arguments, thus splitting mat1 into a list of rank-two tensors but seeing mat2 as a single rank-two tensor (matrix), not a list of rank-one tensors (lists). In this way threading over the list of matrices mat1 works beautifully. $\endgroup$ – Roman Mar 23 at 6:56
  • $\begingroup$ Yeah. In this sense, CompiledFunctions work slightly differently but IMHO more predictably than conventional function with the attribute Listable. $\endgroup$ – Henrik Schumacher Mar 23 at 7:27
1
$\begingroup$

Using Map:

Result = mat2*# & /@ mat1

This can be parallelized for large systems:

Result = Parallelize[mat2*# & /@ mat1]
$\endgroup$
  • 2
    $\begingroup$ Have you really tried and timed the parallelized code? On my machine and with the example from my post, your parallelized code needs about 15 times longer than mat2*# & /@ mat1... $\endgroup$ – Henrik Schumacher Mar 22 at 10:44
  • 1
    $\begingroup$ Yes @HenrikSchumacher I have found the same bizarre result as you. Plus, none of my methods are as fast as yours. $\endgroup$ – Roman Mar 22 at 10:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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