ETOOBUSY 🚀 minimal blogging for the impatient
PWC170 - Kronecker Product
TL;DR
On with TASK #2 from The Weekly Challenge #170. Enjoy!
The challenge
You are given 2 matrices.
Write a script to implement
Kronecker Product
on the given 2 matrices.For more information, please refer wikipedia page.
For example,
A = [ 1 2 ] [ 3 4 ] B = [ 5 6 ] [ 7 8 ] A x B = [ 1 x [ 5 6 ] 2 x [ 5 6 ] ] [ [ 7 8 ] [ 7 8 ] ] [ 3 x [ 5 6 ] 4 x [ 5 6 ] ] [ [ 7 8 ] [ 7 8 ] ] = [ 1x5 1x6 2x5 2x6 ] [ 1x7 1x8 2x7 2x8 ] [ 3x5 3x6 4x5 4x6 ] [ 3x7 3x8 4x7 4x8 ] = [ 5 6 10 12 ] [ 7 8 14 16 ] [ 15 18 20 24 ] [ 21 24 28 32 ]
The questions
The challenge text requests us to implement the product, so I wonder if using modules is OK in this case. I’ll assume that it means in the sense of “implement something that can calculate the product”.
On a similar note, I’d ask if the output format is strict or can be varied a bit… depending on what’s easy to accomplish. Again, I’ll assume that whatever goes, as long as it’s readable.
The solution
These are busy weeks and this usually increments my likelihood to go for simple and lazy solutions. Hence, you can imagine that between learning PDL and using Math::Matrix… I opted for the second:
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use Math::Matrix;
my $A = Math::Matrix->new([1, 2], [3, 4]);
my $B = Math::Matrix->new([5, 6], [7, 8]);
my $K = $A->kron($B);
$K->print("K\n");
I’m not sure I entirely like the output format, but it’s a gift and I don’t want to whine about it.
For the Raku solution, I remembered about the famous Wally Wood:
Never draw anything you can copy, never copy anything you can trace, never trace anything you can cut out and paste up.
This was definitely one of those occasions, as the solution is already there in Rosetta Code. So with very little cosmetics, here we go:
#!/usr/bin/env raku
use v6;
sub MAIN {
.say for kronecker-product([ <1 2>, <3 4> ],
[ <5 6>, <7 8> ]);
}
sub kronecker-product (@a, @b) {
(@a X @b).map: { .[0].list X* .[1].list };
}
Well, I guess I at least owe an explanation. To me.
We start with @a
and @b
being arrays of tuples:
> my @a = <1 2>, <3 4>;
[(1 2) (3 4)]
> my @b = <5 6>, <7 8>;
[(5 6) (7, 8)]
The X
operator creates pairs from the two operands, so it creates
pairs of rows from the two arrays:
> @a X @b
(((1 2) (5 6)) ((1 2) (7 8)) ((3 4) (5 6)) ((3 4) (7 8)))
These pairs are fed into the map
as the implicit variable $_
, which
we don’t even have to mention. Thus, .[0]
and .[1]
are the two rows
coming respectively from @a
and @b
, and we multiply element by
element with X*
. The .list
part allows us tell Raku to do the
right thing with these sequences.
The sub can be put in a slightly more readable way:
sub kronecker-product (@a, @b) {
(@a X @b).map: -> (@A, @B) { @A X* @B };
}
Here we assign each pair from map
to (@A, @B)
, which then
get the respective row from @a
and @b
like before. Here we don’t
even have to use the .list
, which in my humble opinion makes the
solution more readable and shorter.
OK, I’ve not been that lazy after all… Stay safe!