I have defined a 3x3 matrix of functions (all cos(t)
) as follows:
vij = SizedMatrix{3,3}([t->cos(t) for i in 1:3 for j in 1:3])
I’d like to execute the matrix for a particular value of t
, say t=2.
:
values = [vij[i,j](2.) for i in 1:3 for j in 1:3]
This works.
Is there a more idiomatic way to generate this matrix of values, for example something like:
vij(3.)
which I know does not work. I can obviously write a function to do this, but I am wondering if such an approach already exists in the Julia language.
Thanks!
It can be discussed how idiomatic it is, but this is one way to do it:
julia> vij = [t->cos(t) for i in 1:3, j in 1:3]
3×3 Matrix{var"#14#16"}:
#14 #14 #14
#14 #14 #14
#14 #14 #14
julia> 3 .|> vij
3×3 Matrix{Float64}:
-0.989992 -0.989992 -0.989992
-0.989992 -0.989992 -0.989992
-0.989992 -0.989992 -0.989992
1 Like
Cool! I like your solution. I like the pipe operator. I wonder if it is possible without pipes. I had tried, but did not succeed. Thanks!
Not as pretty as the pipe operator, but one could also do
map(f->f(3.0), vij)
jling
May 7, 2023, 4:49pm
5
can be simplified to just
vij = [cos for i in 1:3, j in 1:3]
2 Likes
Timings with @time
:
Pipe operator: 0.00003 sec
@time 3. .|> vij;
map
operator: 0.012
@time map(3., f -> f(3.))
Speed ratio: 400x
The solution should use vij
.
jling
May 7, 2023, 4:50pm
8
I’m saying how you get vij
can be simplified
1 Like
I think it’s a timing artefact, both should be equally fast. (But the pipe is in my opinion the nicer solution anyway )
using BenchmarkTools
@btime 3.0 .|> $vij; # 23.394 ns (1 allocation: 128 bytes)
@btime map(f -> f(3.0), $vij); # 23.092 ns (1 allocation: 128 bytes)
4 Likes
Thanks. The use of $vij
makes a big difference. I had not considered that.
e3c6
May 7, 2023, 5:26pm
11
The variance usually comes from other processes going on in your system unrelated to the thing you want to measure. That’s why BenchmarkTools reports the minimum time.
@time 3 .|> vij;
0.000017 seconds (4 allocations: 208 bytes)
even if I execute the command multiple times.
How is it that @btime
gives a single allocation?
@btime 3. .|> vij;
374.595 ns (4 allocations: 208 bytes)
@btime map(f->f(3.), vij);
121.870 ns (3 allocations: 160 bytes)
The distributions of the timing (e.g. output of @benchmark
including the mean and median times per call) are essentially identical for both cases, so, it’s not just the minimal time which is the same. (Anyway, not so important. I don’t want to distract the main discussion.)
To reply to the last post. There is the interpolation $
used. The problem with timing is that one often calls in the global scope which introduces extra complications. (See Performance Tips · The Julia Language ) With the $
one can inject basically some variable from the global scope such that they behave like local scope variables. That is closer to the setting one usually has when a line of code appears inside a function call.
1 Like
For comparison:
@btime [$vij[i,j](3.) for i in 1:3, j in 1:3]
I learned a lot from all your answers. I simply do not use Julia enough to permanently change my way of thinking. Thanks!
1 Like
Dan
May 7, 2023, 6:35pm
15
Maybe I missed it, but has:
julia> [f(3.0) for f in vij]
3×3 Matrix{Float64}:
-0.989992 -0.989992 -0.989992
-0.989992 -0.989992 -0.989992
-0.989992 -0.989992 -0.989992
been suggested?
Except for performance, the clarity of the code should always be judged in code sections known not to be time critical.
4 Likes
I love this solution because it is the same regardless of the matrix structure!
Efficiency is the same as the other solutions.
@btime [f(3.0) for f in $vij]
21.480 ns (1 allocation: 128 bytes)
Thank you.
1 Like