2

This is a follow-up question to Interpolating literals into each expression in an array using a for loop . In that question, I wanted to interpolate a 1D array (vector) of expressions in a loop. Using the accepted answer:

julia> array_of_expr = Any[:(A * 5), :(B * 6 * T)]

julia> arglist = [:A; :B; :T]
julia> test_input = [1e5, 1e1, 100]

julia> @eval begin
        function interpolate_1by1($(arglist...))
            result_array = similar($array_of_expr)
            $((quote
                result_array[$i] = $(array_of_expr[i])
            end for i in 1:length(array_of_expr))...)
            return result_array
        end
    end

julia> interpolate_all(test_input...)
2-element Array{Float64,1}:
 500000.0
 6000.0

Working off of the accepted answer, suppose I now have an array of vectors of expressions:

args = [:A, :B, :C]
args_typed = [:($s::Float64) for s in args]
    
big_expr_array = Array{Vector{Expr}}(undef, 3, 4)
    
big_expr_array[1, :] = [[:(A+B), :(C/A)], [:(B+1), :(A+C)], [:(B%5)], [:(C+7)]]
big_expr_array[2, :] = [[:(A*B+C), :(B*C+A)], [:(C/6), :(C+C)], [:(3*C)], [:(A%2)]]
big_expr_array[3, :] = [[:(A*C), :(A*B)], [:(A-6), :(B+C/A)], [:(B+2)], [:(C%3)]]

and want to evaluate all the expressions given some arguments, without losing the overall structure of the array. It seems to me I have to loop over the i rows, j columns, and k elements (expression vectors) in the array.

I have tried to modify the accepted answer to the previous question in two ways, both of which produce errors:

@eval begin
    function looped_eval1($(args_typed...))

        result = similar($big_expr_array)
        $( (quote 
            $( (quote 
                $( (quote
                        # i = row, j = vector, k = specific expression
                        result[$i, $j][$k] = $(big_expr_array[i, j][k])
                    end for k in 1:length(big_expr_array[i, j]))...
                 ) 
                end for j in 1:size(big_expr_array)[2])...
             )
            end for i in 1:size(big_expr_array)[1])...
         )
    end
end

this throws UndefRefError: access to undefined reference.

Second:

@eval begin
    function looped_eval2($(args_typed...))
        
        result = similar($big_expr_array)
        $( (quote (quote (quote
                              # i = row, j = vector, k = specific expression
                              result[$i, $j][$k] = $(big_expr_array[i, j][k])
                          end for k in 1:length(big_expr_array[i, j]))... 
                   end for j in 1:size(big_expr_array)[2])...
            end for i in 1:size(big_expr_array)[1])...
         )
    
    end
end

this throws syntax: "..." expression outside call around In[130]:5.

An example desired result for the arguments A = 10, B = 20, C = 30:

big_expr_array[1, :] = [[30, 3], [21, 40], [0], [37]]
big_expr_array[2, :] = [[230, 610], [5, 60], [90], [0]]
big_expr_array[3, :] = [[300, 200], [4, 23], [22], [0]]

How should I write the manual loop unroll to accommodate these nested arrays? Or, is there a more elegant way to achieve evaluation of all of these expressions and maintain the same array structure?

(It's also possible that the code may assign to result correctly--since I'm stuck on the loop unroll, I'm not sure.)

dax
  • 45
  • 5
  • can you explain line `for k in length(big_expr_array[i, j][k])`, you cannot use `k` before defining it – Thibault D. Jul 12 '21 at 21:57
  • I noticed the [k] should not be there, I have corrected the error and updated the original question with some clarification and the actual error thrown. – dax Jul 12 '21 at 22:30
  • you can have a look at [Unrolled.jl](https://github.com/cstjean/Unrolled.jl) – MarcMush Jul 13 '21 at 08:59

1 Answers1

2

Here is something working:

@eval begin                                                                                                                                                      
    function looped_eval2($(args_typed...))                                                                                                                                                                                                                                                                                       
        result = similar($big_expr_array, Vector{Float64})                                                                                                           
        result .= [[]]                                                                                                                                               
        $( (quote                                                                                                                                                                          
                # i = row, j = vector, k = specific expression                                                                                                 
                push!(result[$i, $j], $(expr))                                                                                                                           
            end                                                                                                                                                                                                                                                                                                                       
            for i in 1:size(big_expr_array, 1)                                                                                                                           
            for j in 1:size(big_expr_array, 2)                                                                                                                           
            for expr in big_expr_array[i, j]                                                                                                                                  
            )...                                                                                                                                                                                    
        )                                                                                                                                                           
        result                                                                                                                                                   
    end                                                                                                                                                      
end

A few modifications are necessary over your prior work:

  • You must type annotate and initialize result otherwise you get an UndefRefError when you try to access any element of the result;
  • When you are unrolling the loops, only one quote is necessary, all 3 loops are outside the quote and the whole list of generated expressions are then evaluated;
  • Because all vectors inside your big_expr_array are not the same size, the type of the result is a bit complicated (Matrix{Vector{Float}}) and you must use push! inside the quote instead of direct definition of the value.

The result is what you expected:

julia> looped_eval2(10., 20., 30.)                                                                                                                           
3×4 Matrix{Vector{Float64}}:                                                                                                                                  
    [30.0, 3.0]     [21.0, 40.0]  [0.0]   [37.0]                                                                                                                 
    [230.0, 610.0]  [5.0, 60.0]   [90.0]  [0.0]                                                                                                                  
    [300.0, 200.0]  [4.0, 23.0]   [22.0]  [0.0]

A better Julia version of the same code (and slightly better according to BenchmarkTools) would be:

@eval begin                                                                                                                                                      
    function looped_eval3($(args_typed...))                                                                                                                                                                                                                                                                                       
        result = map(_ -> Float64[], $big_expr_array)                                                                                                                
        $( (quote                                                                                                                                                            
                # i = row, j = vector, k = specific expression                                                                                                               
                push!(result[$i], $(expr))                                                                                                                               
            end                                                                                                                                                          
            for (i, expr_l) in enumerate(big_expr_array)                                                                                                                 
            for expr in expr_l                                                                                                                                           
            )...                                                                                                                                                      
        )                                                                                                                                                           
        result                                                                                                                                                   
    end                                                                                                                                                      
end
Thibault D.
  • 1,567
  • 10
  • 23
  • Thanks, what is the meaning of the syntax "map(_ -> Float64 []", specifically the underscore? I haven't seen that in Julia before. I wouldn't even know how to search for that in the documentation... searching "_" produces nothing useful. – dax Jul 13 '21 at 16:47
  • Also, yes, I will have to see if the performance is okay in the real code, in which the vectors might be pretty long (on the order of 100-500 elements), as they're part of a complex chemical network. If there are problems I will have to see if I can predict the length of them in some way... thank you! – dax Jul 13 '21 at 16:56
  • 1
    _ is just the name of a variable I don't use. `map` is a function that apply another function to all values of its second argument. This statement creates a matrix similar to big_expr_array full of empty float vector – Thibault D. Jul 13 '21 at 17:56
  • ah ok, I found the information here: https://discourse.julialang.org/t/usage-rules-for-bare-underscores/28858 thank you – dax Jul 13 '21 at 18:52