# Problem with parentheses around quoted expression in generated function

Hello everyone.

I’m trying to use generated functions to write functions that would return the sum of the values of the nearest neighbors of given site in a multidimensional integer periodic lattice.

For example: In a periodic 2D lattice, the sum of the values of the nearest neighbors of the site s would be

``````lattice[mod1(s[1] - 1, size(lattice, 1)), s[2]] + lattice[mod1(s[1] + 1, size(lattice, 1)), s[2]] + lattice[s[1], mod1(s[2] - 1, size(lattice, 2))] + lattice[s[1], mod1(s[2] + 1, size(lattice, 2))]
``````

My attempt

Reading through the examples provided in the Manual, I came up with the following:

``````# Sum of nearest neighbors of site \$s\$ in a periodic integer lattice
function square_lattice_nn_sum_impl(lattice::Type{Array{Int,dims}}, s::Type{NTuple{dims,Int}}) where dims
# Accumulate final expression
ex = :()
# Loop on the dimensions
for d in 1:dims
# Indices for both nearest neighbors in the current dimension
idx_prev_nn = :(mod1(s[\$d] - 1, size(lattice, \$d)))
idx_next_nn = :(mod1(s[\$d] + 1, size(lattice, \$d)))
# Fill indices before the current dimension
idx_before = :()
for k in d-1:-1:1
idx_before = :(s[\$k], \$idx_before)
end
# Fill indices after the current dimension
idx_after = :()
for k in d+1:dims
idx_after = :(\$idx_after, s[\$k])
end
# Accumulate sum
ex = :(\$ex + lattice[\$idx_before \$idx_prev_nn \$idx_after] + lattice[\$idx_before \$idx_next_nn \$idx_after])
end
return :(\$ex)
end

# Extract generated function to the body of a regular function
@generated function square_lattice_nn_sum(lattice::Array{Int,dims}, s::NTuple{dims,Int}) where dims
square_lattice_nn_sum_impl(lattice, s)
end
``````

For the 2D case, my program returns the following expression:

``````julia> square_lattice_nn_sum_impl(Array{Int,2}, NTuple{2,Int})

:((() + lattice[() mod1(s[1] - 1, size(lattice, 1)) ((), s[2])] + lattice[() mod1(s[1] + 1, size(lattice, 1)) ((), s[2])]) + lattice[(s[1], ()) mod1(s[2] - 1, size(lattice, 2)) ()] + lattice[(s[1], ()) mod1(s[2] + 1, size(lattice, 2)) ()])
``````

Which would be the desired result if it wasn’t for the several misplaced parentheses.

My questions

Is it possible to accumulate quoted expression without parentheses around them?

Is there a better way of achieving this?

Are generated functions even the right tool for this job?

Thanks.

There are other ways but I’d make arrays of expressions, and splat them. Rather than try to recursively build what aren’t really recursive expressions.

``````julia> function new_short(lattice::Type{Array{Int,dims}}, s::Type{NTuple{dims,Int}}) where dims
terms = map(1:dims) do d
idx_prev_nn = :(mod1(s[\$d] - 1, size(lattice, \$d)))
idx_next_nn = :(mod1(s[\$d] + 1, size(lattice, \$d)))

idx_before = [:(s[\$k]) for k in d-1:-1:1]

term = :(lattice[\$(idx_before...), \$idx_prev_nn] + lattice[\$(idx_before...), \$idx_next_nn])
end
return :(+(\$(terms...)))
end;

julia> new_short(Array{Int,2}, NTuple{2,Int})
:((lattice[mod1(s[1] - 1, size(lattice, 1))] + lattice[mod1(s[1] + 1, size(lattice, 1))]) + (lattice[s[1], mod1(s[2] - 1, size(lattice, 2))] + lattice[s[1], mod1(s[2] + 1, size(lattice, 2))]))

julia> new_short(Array{Int,3}, NTuple{3,Int})
:((lattice[mod1(s[1] - 1, size(lattice, 1))] + lattice[mod1(s[1] + 1, size(lattice, 1))]) + (lattice[s[1], mod1(s[2] - 1, size(lattice, 2))] + lattice[s[1], mod1(s[2] + 1, size(lattice, 2))]) + (lattice[s[2], s[1], mod1(s[3] - 1, size(lattice, 3))] + lattice[s[2], s[1], mod1(s[3] + 1, size(lattice, 3))]))

julia> function orig_short(lattice::Type{Array{Int,dims}}, s::Type{NTuple{dims,Int}}) where dims
ex = :()  # Trimming the original function to the same degree
for d in 1:dims
idx_prev_nn = :(mod1(s[\$d] - 1, size(lattice, \$d)))
idx_next_nn = :(mod1(s[\$d] + 1, size(lattice, \$d)))
idx_before = :()
for k in d-1:-1:1
idx_before = :(s[\$k], \$idx_before)
end
ex = :(\$ex + lattice[\$idx_before \$idx_prev_nn] + lattice[\$idx_before \$idx_next_nn])
end
return :(\$ex)
end
orig_short (generic function with 1 method)

julia> orig_short(Array{Int,2}, NTuple{2,Int})
:((() + lattice[() mod1(s[1] - 1, size(lattice, 1))] + lattice[() mod1(s[1] + 1, size(lattice, 1))]) + lattice[(s[1], ()) mod1(s[2] - 1, size(lattice, 2))] + lattice[(s[1], ()) mod1(s[2] + 1, size(lattice, 2))])
``````
1 Like

You don’t need a generated function for this. See: Seemingly unnecessary allocation within for loop - #8 by stevengj

2 Likes