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

296 views Asked by At

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.

4

There are 4 answers

11
Shayan On BEST ANSWER

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
0
phipsgabler On

There is no syntax error, but you never assign to an array. You only overwrite the local scalar ol in every iteration.

To get your desired result, you need to construct an actual matrix. Probably a matrix comprehension is the most readable short version (using plain floats, as I don't have your interval package loaded):

julia> α = [200, 225, 250, 275]; γ = [2, 2.25, 2.5, 2.75];

julia> [a / g for a in α, g in γ]
4×4 Matrix{Float64}:
 100.0   88.8889   80.0   72.7273
 112.5  100.0      90.0   81.8182
 125.0  111.111   100.0   90.9091
 137.5  122.222   110.0  100.0

You can also use broadcasting, but then you have to change the orientation of one of the vectors:

julia> α ./ permutedims(γ)
4×4 Matrix{Float64}:
 100.0   88.8889   80.0   72.7273
 112.5  100.0      90.0   81.8182
 125.0  111.111   100.0   90.9091
 137.5  122.222   110.0  100.0
1
AboAmmar On

Julia's broadcasting machinery is so powerful, use the dot notation with transpose to divide the vectors the way you want. This gives you a matrix, convert to a vector if you wish using vec.

α = [200, 225, 250, 275]; γ = [2, 2.25, 2.5, 2.75];
vec(α' ./ γ)

The same code works with IntervalArithmetic.jl:

using IntervalArithmetic

α = [200..225, 225..250, 250..275, 275..300]
γ = [2..2.25, 2.25..2.5, 2.5..2.75, 2.75..3]

vec(α' ./ γ)
16-element Vector{Interval{Float64}}:
        [88.8888, 112.5]
  [80, 100]
        [72.7272, 90]
        [66.6666, 81.8182]
 [100, 125]
   [90, 111.112]
        [81.8181, 100]
    [75, 90.9091]
       [111.111, 137.5]
  [100, 122.223]
        [90.909, 110]
        [83.3333, 100]
       [122.222, 150]
  [110, 133.334]
 [100, 120]
        [91.6666, 109.091]
1
Dan Getz On

There are two Interval arithmetic packages in Julia eco-system: Intervals.jl and IntervalArithmetic.jl. The latter provides a pre-defined / operator (which satisfies interval guarantees). So let's assume the latter is used.

Additionally, the vectors specified are Float64 but from the actual values, the numbers 'want' to be Rational. Making the numbers Rational turns out to step on some missing convert methods in IntervalArithmetic, but this is easily fixed. So:

using IntervalArithmetic                                                                                          
                                                                                                                         
α = [(200..225); (225..250); (250..275); (275..300)]                                                              
γ = [(2..2.25); (2.25..2.5); (2.5..2.75); 

alpha = Interval.(zip((rationalize.(getproperty.(α,k)) for k in [:lo, :hi])...))
gamma = Interval.(zip((rationalize.(getproperty.(γ,k)) for k in [:lo, :hi])...))

permutedims(alpha) ./ gamma  # like other answers suggested

This returns:

4×4 Matrix{Interval{Rational{Int64}}}:
  [800//9, 225//2]   [100//1, 125//1]   [1000//9, 275//2]   [1100//9, 150//1]
   [80//1, 100//1]   [90//1, 1000//9]   [100//1, 1100//9]    [110//1, 400//3]
  [800//11, 90//1]  [900//11, 100//1]  [1000//11, 110//1]    [100//1, 120//1]
 [200//3, 900//11]  [75//1, 1000//11]    [250//3, 100//1]  [275//3, 1200//11]