Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a fast way to expand matrices n times by duplicating each line?

For example, [1 1 ; 2 2 ; 3 3] becomes

[1 1
 1 1
 1 1
 2 2
 2 2
 2 2
 3 3
 3 3
 3 3]

I am using this: expander(orig,mult::Int) = orig[ceil(Int,(1:size(orig,1)*mult)/mult),:]; in Julia and the following in Matlab:

function expanded = expander(original,multiplier)
   expanded = original(ceil((1:size(original,1)*multiplier)/multiplier),:);
end

Another matlab only way to do it is this:

expanded = kron(original,ones(multiplier,1));

I would prefer a superfast julia option if it exists.

like image 920
BFH Avatar asked Nov 30 '25 04:11

BFH


2 Answers

This doesn't prove that kron is fastest, but I compared its time to how long it would just take to populate a similarly sized Array with ones, and kron did quite well:

original = [1 1 ; 2 2 ; 3 3];
multiplier = 3*10^6;

@time begin
    for idx = 1:100
        expanded = kron(original,ones(multiplier));
    end
end
## 9.199143 seconds (600 allocations: 15.646 GB, 9.05% gc time)

@time begin
    for idx = 1:100
        myones = [ones(multiplier*size(original,1))  ones(multiplier*size(original,1))];
    end
end
## 12.746123 seconds (800 allocations: 26.822 GB, 14.86% gc time)

Update In response to comments by David Sanders, here are tests wrapped in a function. The reason I did the tests globally, which I know isn't normal best practice, is because it seemed quite plausible to me that the objects might get created globally.

function kron_test(original, multiplier)
    for idx = 1:100
        expanded = kron(original,ones(multiplier));
    end
end

function ones_test(original, multiplier)
    for idx = 1:100
        myones = [ones(multiplier*size(original,1))  ones(multiplier*size(original,1))];
    end
end

## times given after first function call to compile
@time kron_test(original, multiplier);  ## 11.107632 seconds (604 allocations: 15.646 GB, 23.98% gc time)
@time ones_test(original, multiplier);  ## 15.849761 seconds (604 allocations: 26.822 GB, 33.50% gc time)
like image 144
Michael Ohlrogge Avatar answered Dec 02 '25 20:12

Michael Ohlrogge


Personally, I'd just use repeat:

repeat(original, inner=(multiplier, 1))

Unlike kron, it's very readable and understandable. Unfortunately it is quite a bit slower. Even so, I'd only use kron if you've identified it as a performance bottleneck. While it's faster for computers to execute, it's much slower for humans to understand what's going on… and the performance of repeat should eventually get better (it's issue #15553).


Edit: As of Julia 1.2, repeat has indeed gotten significantly faster. It now rivals kron:

julia> @btime kron($original,ones($multiplier));
  81.039 ms (6 allocations: 160.22 MiB)

julia> @btime repeat($original, inner=($multiplier, 1));
  84.087 ms (27 allocations: 137.33 MiB)
like image 32
mbauman Avatar answered Dec 02 '25 20:12

mbauman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!