Dictionary is changing its values with matrices

Hello World !
I would to share an experience that google did not provide useful answers at first. Therefore, maybe someone in the future can save your time reading this words.

I’m coding a program of orbits of planets in solar system. To do so, I work with a matrix to store the (x,y) position of each planet at each moment.

To save the final result, I’m storing into a dictionary. However, the values inside the dictionary will change. Here’s the code with many prints - only for 1 planet:

GMₛ = 4π^2
r(x,y)  = sqrt(x^2  + y^2)
function computeOrbit_allPlanets(R, v₀; Δt = 0.0001, nMax = 1e7)
    dic_xy = []
    nPlanets = length(R)

    matrix_xy_old = zeros(nPlanets,2)
    matrix_xy_old[:,1] .= R
    push!(dic_xy, matrix_xy_old)

    matrix_xy_current = copy(matrix_xy_old)
    matrix_xy_current[:,2] .+= v₀*Δt
    push!(dic_xy, matrix_xy_current)
    
    println(" Begin :")
    println("first two : ", dic_xy)

    matrix_xy_new = zeros(nPlanets,2)
    acc = zeros(nPlanets,2)
    for n in 3:nMax
        for i in 1:nPlanets
            acc[i,1] = get_acc_planet_i(i, matrix_xy_current, true)
            acc[i,2] = get_acc_planet_i(i, matrix_xy_current, false)
        end
        for i in 1:nPlanets
            matrix_xy_new[i,1] = 2matrix_xy_current[i,1] - matrix_xy_old[i,1] + acc[i,1]*Δt^2
            matrix_xy_new[i,2] = 2matrix_xy_current[i,2] - matrix_xy_old[i,2] + acc[i,2]*Δt^2
        end

        push!(dic_xy, matrix_xy_new)
matrix_xy_old[:,2] = matrix_xy_current[:,2]
        matrix_xy_old[:,1] = matrix_xy_current[:,1]

        matrix_xy_current[:,2] = matrix_xy_new[:,2]
        matrix_xy_current[:,1] = matrix_xy_new[:,1]
        println("$(n) -- inside: ", dic_xy)
    end
    println("******")
    println("outside: ", dic_xy)
    return dic_xy
end
function get_acc_planet_i(i, matrix_xy, isX)
    x_i = matrix_xy[i,1]
    y_i = matrix_xy[i,2]
    if isX==true
        aa = -GMₛ*x_i/r(x_i, y_i)^3
    else
        aa = -GMₛ*y_i/r(x_i, y_i)^3
    end
    for j = 1:length(matrix_xy[:,1])
        if j≠i
            x_j = matrix_xy[j,1]
            y_j = matrix_xy[j,2]
            if isX==true
                aa -= massPlanets[j]*(x_i-x_j)/r(x_i-x_j, y_i-y_j)^3
            else
                aa -=  massPlanets[j]*(y_i-y_j)/r(x_i-x_j, y_i-y_j)^3
            end
        end
    end
    return aa
end

Output :

# For only one planet
computeOrbit_allPlanets(1, 2π; Δt = 0.0010, nMax = 5)
 Begin :
first two : Any[[1.0 0.0], [1.0 0.006283185307179587]]
3 -- inside: Any[[1.0 0.006283185307179587], [0.9999605239200985 0.01256612257883395], [0.9999605239200985 0.01256612257883395]]
4 -- inside: Any[[0.9999605239200985 0.01256612257883395], [0.9998815756560362 0.0188485638186023], [0.9998815756560362 0.0188485638186023], [0.9998815756560362 0.0188485638186023]]
5 -- inside: Any[[0.9998815756560362 0.0188485638186023], [0.999763160661069 0.025130261079069214], [0.999763160661069 0.025130261079069214], [0.999763160661069 0.025130261079069214], [0.999763160661069 0.025130261079069214]]
******
outside: Any[[0.9998815756560362 0.0188485638186023], [0.999763160661069 0.025130261079069214], [0.999763160661069 0.025130261079069214], [0.999763160661069 0.025130261079069214], [0.999763160661069 0.025130261079069214]]
5-element Array{Any,1}:
 [0.9998815756560362 0.0188485638186023]
 [0.999763160661069 0.025130261079069214]
 [0.999763160661069 0.025130261079069214]
 [0.999763160661069 0.025130261079069214]
 [0.999763160661069 0.025130261079069214]

The solution was to make a “push!(dic_xy, copy(matrix_xy_new))”.

Maybe discourse could create a category “Random Example”. But I don’t know where to write examples likes this for a broader community.

1 Like

Note that this is why python doesn’t allow mutable dictionary keys.

Note that this is why python doesn’t allow mutable dictionary keys.

Actually, there’s no Dict here–just a Vector.

@Noel_Araujo your solution is correct–the reason is that doing push!(my_list, my_value) will not copy my_value. That means that if you do:

x = [1, 2, 3]
v = []
push!(v, x)
push!(v, x)
push!(v, x)

then the three entries in v will all be the same object as x. Try changing x after doing that and note how all the elements of v change as well:

julia> x[1] = 5
5                                                                                                                                                                
                                                                                                                                                                 
julia> v
3-element Array{Any,1}:                                                                                                                                          
 [5, 2, 3]                                                                                                                                                       
 [5, 2, 3]                                                                                                                                                       
 [5, 2, 3] 

By the way, I would suggest referring to your dict_xy as a “list” or “vector” rather than a “dictionary”. In Julia, “dictionary” generally refers to the Dict type, which is an associative container, while you’re using the Vector type which you get from [].

2 Likes