Search code examples
juliainterval-arithmetic

Storing elements in a Vector using "for" loop in Julia


I aim to divide elements of two different interval vectors, alpha, and gamma, and want to store the resultant intervals in a vector form. I am trying to run this code in Pluto-Julia but not getting the results:

using IntervalArithmetic, Plots, PlutoUI, Arblib, RecursiveArrayTools, StaticArrays

begin
    α = [(200..225); (225..250); (250..275); (275..300)]
    γ = [(2..2.25); (2.25..2.5); (2.5..2.75); (2.75..3)]
    local kk = 0
    for i in 1:4
        for j in 1:4
            ol = α[i]/γ[j]
            kk = kk + 1
        end
    end
    @show ol
end

I expect that the resultant interval vector should contain 16 elements (4*4). I think there is a syntax error. I would be great help for me if code is provided for generalized case, means i and j will not be just 4 and 4 but can be changed.


Solution

  • I don't know why you are using a begin block, but if you intend to use a hard scope, the best choice is to wrap it in a function (Also, it's possible to use the let block, but I won't recommend it generally) and the bein blocks do not introduce a new scope.

    1. The first problem with your code is using the double dots, which isn't meaningful in Julia for separating elements.

    α = [(200..225); (225..250); (250..275); (275..300)]

    Instead, you can separate elements of a container via comma or semicolon. In your case, you can do it like this:

    julia> α = [(200, 225); (225, 250); (250, 275); (275, 300)]
    4-element Vector{Tuple{Int64, Int64}}:
     (200, 225)
     (225, 250)
     (250, 275)
     (275, 300)
    
    1. Since the begin blocks do not introduce a new scope (read here about scopes in Julia), then you don't need to declare the local variable (unless there is further code that you didn't provide in the question).
    2. If you are intended to store the result of an arithmetic operation using the for loop in a container:
      One way is first to define initialized containers and then update their values (as it seems you intended such an approach. Although the structure can be improved much more, I won't change the overall code structure of OP, but the content in the following code block):
    julia> begin
               α = [(1, 5); (5, 10); (10, 15); (15, 20)]
               β = [(3, 6); (6, 9); (9, 12); (12, 15)]
               kk = 0
    
               ol = Matrix{NTuple{2, Float64}}(undef, 4, 4)
               for i in 1:4
                   for j in 1:4
                       ol[i, j] = (α[i][1]/β[j][1], α[i][2]/β[j][2])
                       kk += 1
                   end
               end
           end
    
    julia> ol
    4×4 Matrix{Tuple{Float64, Float64}}:
     (0.333333, 0.833333)  (0.166667, 0.555556)  (0.111111, 0.416667)  (0.0833333, 0.333333)
     (1.66667, 1.66667)    (0.833333, 1.11111)   (0.555556, 0.833333)  (0.416667, 0.666667)
     (3.33333, 2.5)        (1.66667, 1.66667)    (1.11111, 1.25)       (0.833333, 1.0)
     (5.0, 3.33333)        (2.5, 2.22222)        (1.66667, 1.66667)    (1.25, 1.33333)
    
    julia> kk
    16
    

    Note that the result is a 4*4 Matrix (as you expected) with 16 elements.

    • Q1: What is the meaning of ol = Matrix{NTuple{2, Float64}}(undef, 4, 4)?
      Here, I defined an Initialized Matrix with a size of 4*4 in which elements are Tuples with a length of 2 and the Float64 element type.
    • Q2: What is happening in the ol[i, j] = (α[i][1]/β[j][1], α[i][2]/β[j][2])?
      Here, I'm trying to divide the first member of the i'th element of the α by the first member of the j'th element of the β (in the α[i][1]/β[j][1]), and the second member of the i'th element of the α by the second member of the j'th element of the β (in the α[i][2]/β[j][2]) and replace the result with the [i, j] element of the ol.

    Update

    Here is a similar structure using the IntervalArithmetic.jl package:

    using IntervalArithmetic
    
    begin
        α = [(1..5); (5..10); (10..15); (15..20)]
        β = [(3..6); (6..9); (9..12); (12..15)]
        kk = 0
    
        ol = Matrix{Interval{Float64}}(undef, 4, 4)
        for i in 1:4
            for j in 1:4
                ol[i, j] = α[i]/β[j]
                kk += 1
            end
        end
    end
    

    Then if I check for the ol and the kk:

    julia> ol
    4×4 Matrix{Interval{Float64}}:
     [0.166666, 1.66667]  [0.111111, 0.833334]  [0.0833333, 0.555556]     [0.0666666, 0.416667]
     [0.833333, 3.33334]  [0.555555, 1.66667]   [0.416666, 1.11112]       [0.333333, 0.833334]
     [1.66666, 5]         [1.11111, 2.5]        [0.833333, 1.66667]       [0.666666, 1.25]
     [2.5, 6.66667]       [1.66666, 3.33334]    [1.25, 2.22223]        [1, 1.66667]
    
    julia> typeof(ol[1, 1])
    Interval{Float64}
    
    julia> kk
    16
    

    Since this approach is what you desire, we can write it least a little bit better. First, I begin by defining a function. Second, we can use Iterators.product to avoid nested for loop:

    function div_intervals(first_itv::T, second_itv::T) where T<:Vector{Interval{Float64}}
        m, n = length(first_itv), length(second_itv)
        ol = Matrix{Interval{Float64}}(undef, m, n)
    
        for (i_idx, j_idx) in Iterators.product(1:m, 1:n)
            ol[i_idx, j_idx] = first_itv[i_idx]/second_itv[j_idx]
        end
    
        return ol
    end
    

    This function is written dynamic and can be applied on the α and β with the type of Vector{Interval{Float64}} in any length. Also, we can make it even better by using broadcasting in a for loop:

    function div_intervals2(first_itv::T, second_itv::T) where T<:Vector{Interval{Float64}}
        m, n = length(first_itv), length(second_itv)
        ol = Matrix{Interval{Float64}}(undef, m, n)
    
        for j_idx in eachindex(second_itv)
            ol[:, j_idx] .= first_itv./second_itv[j_idx]
        end
    
        return ol
    end
    
    julia> div_intervals2(α, β)
    4×4 Matrix{Interval{Float64}}:
     [0.166666, 1.66667]  [0.111111, 0.833334]  [0.0833333, 0.555556]     [0.0666666, 0.416667]
     [0.833333, 3.33334]  [0.555555, 1.66667]   [0.416666, 1.11112]       [0.333333, 0.833334]
     [1.66666, 5]         [1.11111, 2.5]        [0.833333, 1.66667]       [0.666666, 1.25]
     [2.5, 6.66667]       [1.66666, 3.33334]    [1.25, 2.22223]        [1, 1.66667]
    
    julia> div_intervals2(α, β) == div_intervals(α, β)
    true