Hello everyone,
Custom Macro
I am hoping to be able to write a macro that can call @tullio to specify an arbitrary sized linear form. I think this should follow from some easy-ish fix similar to an old thread… but I am stuck.
Mathematical Description
I have the input arrays A \in \underbrace{d \times \cdots \times d}_{M +1 \text{ times}}, e \in d, and the output array of b\in d. In Einstein sum notation I want the following mathematical operation:
I can accomplish this using through a string manipulation and Meta.parse, but this does not compile down because the expression produced is evaluated at run time each time it is called.
I have tried craft my own macro following the documentation, but the macro that I have made does not modify appear to be performing the mathematical operation on the data structures that I intended.
Minimum Working Example
using Tullio, Random
##
macro myMacro(b, A, e, M)
return quote
local val_a_max = $M
local args1 = [Symbol("a$j") for j = 1:val_a_max]
# @info "args1 = $args1"
local args2 = [(:ref, esc(e), Symbol("d$j")) for j = 1:val_a_max]
# @info "args2 = $args2"
expr = esc(:(@tullio esc(b)[i] = prod(esc(A)[i, $(args1...)], $(args2...))))
# Meta.show_sexpr(expr)
# println()
expr
end
end
#
function test_this()
Random.seed!(1)
d = 2
M_max = 5
M = nothing
A = nothing
e = nothing
b = zeros(d)
for a in 2:M_max
global M = a
global A = rand((d for _ in 1:M+1)...)
global e = rand(d)
global b = zeros(d)
@info "M = $M"
# my custom macro
@info "- myMacro"
@time @myMacro b A e M
@info "-- $(b')"
# hard coded calls to tullio macro
@info "- hard coded tullio macro"
@time begin
if M == 2
@tullio b[i] = A[i,a1,a2]*e[a1]*e[a2]
elseif M == 3
@tullio b[i] = (A[i,a1,a2,a3]
*e[a1]*e[a2]*e[a3])
elseif M == 4
@tullio b[i] = (A[i,a1,a2,a3,a4]
*e[a1]*e[a2]*e[a3]*e[a4])
elseif M == 5
@tullio b[i] = (A[i,a1,a2,a3,a4,a5]
*e[a1]*e[a2]*e[a3]*e[a4]*e[a5])
elseif M == 6
@tullio b[i] = (A[i,a1,a2,a3,a4,a5,a6]
*e[a1]*e[a2]*e[a3]*e[a4]*e[a5]*e[a6])
elseif M == 7
@tullio b[i] = (A[i,a1,a2,a3,a4,a5,a6,a7]
*e[a1]*e[a2]*e[a3]*e[a4]*e[a5]*e[a6]*e[a7])
elseif M == 8
@tullio b[i] = (A[i,a1,a2,a3,a4,a5,a6,a7,a8]
*e[a1]*e[a2]*e[a3]*e[a4]*e[a5]*e[a6]*e[a7]*e[a8])
elseif M == 9
@tullio b[i] = (A[i,a1,a2,a3,a4,a5,a6,a7,a8,a9]
*e[a1]*e[a2]*e[a3]*e[a4]*e[a5]*e[a6]*e[a7]*e[a8]*e[a9])
elseif M == 10
@tullio b[i] = (A[i,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10]
*e[a1]*e[a2]*e[a3]*e[a4]*e[a5]*e[a6]*e[a7]*e[a8]*e[a9]*e[a10])
else
@error "not implemented"
end
end
@info "-- $(b')"
# eval expr
b = zeros(d)
cmd_str = "@tullio b[i] = A[i"*prod(",a$j" for j in 1:M)*"]"*prod("*e[a$j]" for j in 1:M)
expr = Meta.parse(cmd_str)
@info "- eval expr"
@time eval(expr)
@info "-- $(b')"
println("=============================================================")
end
end
##
# first time everything jit compiles
println("First evaluation of the test. We expect everything to have high compilation time")
test_this()
#
println()
println()
# now only the Meta.parse is evaluating at run time
println("Second evaluation of the test. We expect only Meta.parse to have compilation time")
test_this()
Output
First evaluation of the test. We expect everything to have high compilation time
[ Info: M = 2
[ Info: - myMacro
0.020640 seconds (90.94 k allocations: 4.514 MiB, 99.45% compilation time)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.118539 seconds (474.54 k allocations: 24.213 MiB, 3.53% gc time, 99.81% compilation time)
[ Info: -- [0.6458665226117443 0.8712762271312573]
[ Info: - eval expr
0.107026 seconds (322.60 k allocations: 16.671 MiB, 96.68% compilation time)
[ Info: -- [0.6458665226117443 0.8712762271312573]
=============================================================
[ Info: M = 3
[ Info: - myMacro
0.000021 seconds (76 allocations: 2.688 KiB)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.141966 seconds (474.08 k allocations: 24.201 MiB, 99.93% compilation time)
[ Info: -- [0.047684484532381215 0.055926908789111836]
[ Info: - eval expr
0.138103 seconds (370.36 k allocations: 18.921 MiB, 2.69% gc time, 97.45% compilation time)
[ Info: -- [0.047684484532381215 0.055926908789111836]
=============================================================
[ Info: M = 4
[ Info: - myMacro
0.000032 seconds (90 allocations: 3.234 KiB)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.181469 seconds (557.63 k allocations: 28.450 MiB, 99.95% compilation time)
[ Info: -- [0.24707741708999534 0.3316466084333281]
[ Info: - eval expr
0.170222 seconds (435.33 k allocations: 22.167 MiB, 97.76% compilation time)
[ Info: -- [0.24707741708999534 0.3316466084333281]
=============================================================
[ Info: M = 5
[ Info: - myMacro
0.000021 seconds (104 allocations: 3.719 KiB)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.245771 seconds (628.29 k allocations: 31.886 MiB, 1.32% gc time, 99.96% compilation time)
[ Info: -- [0.06775196978697368 0.09599998137184107]
[ Info: - eval expr
0.237658 seconds (494.51 k allocations: 24.935 MiB, 98.09% compilation time)
[ Info: -- [0.06775196978697368 0.09599998137184107]
=============================================================
Second evaluation of the test. We expect only Meta.parse to have compilation time
[ Info: M = 2
[ Info: - myMacro
0.000013 seconds (62 allocations: 2.203 KiB)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.000013 seconds (12 allocations: 224 bytes)
[ Info: -- [0.6458665226117443 0.8712762271312573]
[ Info: - eval expr
0.101354 seconds (322.58 k allocations: 16.674 MiB, 96.98% compilation time)
[ Info: -- [0.6458665226117443 0.8712762271312573]
=============================================================
[ Info: M = 3
[ Info: - myMacro
0.000016 seconds (76 allocations: 2.688 KiB)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.000013 seconds (15 allocations: 272 bytes)
[ Info: -- [0.047684484532381215 0.055926908789111836]
[ Info: - eval expr
0.136961 seconds (370.35 k allocations: 18.923 MiB, 2.04% gc time, 97.48% compilation time)
[ Info: -- [0.047684484532381215 0.055926908789111836]
=============================================================
[ Info: M = 4
[ Info: - myMacro
0.000023 seconds (90 allocations: 3.234 KiB)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.000017 seconds (18 allocations: 336 bytes)
[ Info: -- [0.24707741708999534 0.3316466084333281]
[ Info: - eval expr
0.177276 seconds (435.32 k allocations: 22.165 MiB, 97.77% compilation time)
[ Info: -- [0.24707741708999534 0.3316466084333281]
=============================================================
[ Info: M = 5
[ Info: - myMacro
0.000022 seconds (104 allocations: 3.719 KiB)
[ Info: -- [0.0 0.0]
[ Info: - hard coded tullio macro
0.000016 seconds (21 allocations: 384 bytes)
[ Info: -- [0.06775196978697368 0.09599998137184107]
[ Info: - eval expr
0.233445 seconds (494.50 k allocations: 24.936 MiB, 1.05% gc time, 98.06% compilation time)
[ Info: -- [0.06775196978697368 0.09599998137184107]
=============================================================