Can I modify the fields of a designed structure?

I was working on a structure with theirs fields are vectors and matrices and I have a vector of it. I need to change elements of this vector to be of a structure which its fields are static vectors and matrices. How can I do that?

I’m not sure what is your doubt, but be careful in that:

julia> isconcretetype(MVector{Float64})
false

julia> isconcretetype(MVector{3,Float64})
true

julia> isconcretetype(MMatrix{Float64})
false

julia> isconcretetype(MMatrix{3,3,Float64,9})
true


(meaning that your RRs structure has abstract fields as it is)

Concerning the rest, I don’t think I get what you want, but maybe something like this:

julia> struct A
           x::Vector{Float64}
       end

julia> struct B
           x::SVector{3,Float64}
       end

julia> B(a::A) = B(SVector{3,Float64}(a.x...))
B

julia> a = A(rand(3))
A([0.8719748854921427, 0.48118038068468383, 0.9809479464441135])

julia> B(a)
B([0.8719748854921427, 0.48118038068468383, 0.9809479464441135])

julia> v = [ A(rand(3)), A(rand(3)) ]
2-element Vector{A}:
 A([0.3015634495239551, 0.6358999576371009, 0.09630161737144594])
 A([0.5218204629518881, 0.289523727629152, 0.16346835337852283])

julia> B.(v)
2-element Vector{B}:
 B([0.3015634495239551, 0.6358999576371009, 0.09630161737144594])
 B([0.5218204629518881, 0.289523727629152, 0.16346835337852283])


2 Likes

You don’t need the MVector(....) call at all when constructing B. You can write this as simply as:

B(a::A) = B(a.x, a.d)

You can also automatically generate a function like this with @eval:

@eval B(a::A) = B($([:(a.$x) for x in fieldnames(A)]...))
2 Likes

Thank you very much!
One thing please, are the below structures are identical?

struct B1
           d::SVector{3,Float64}
end

struct B2
           d::MVector{3,Float64}
end

mutable struct B3
           d::SVector{3,Float64}
end

mutable struct B4
           d::MVector{3,Float64}
end

In which sense? What do you mean by identical?

Sorry if the word was not clear. Actually, I could not recognize the differences between them.
In my code, I have a vector of structure Bx and I am modifying its fields, such as Bx[i].d = rand(3,3)*rand(3). Could you please explain to me which structure is better in my case (in terms of performance).

From where VVector comes? I am not aware of this data structure. About the differences:

  1. You can only do something like Bx[i].d = rand(3,3)*rand(3), i.e., replacing the whole array in a swoop, if the struct you define is mutable.
  2. If your struct is not mutable, then you can yet change the values inside the matrix if the matrix type is mutable. MVector is mutable and, while you cannot replace the whole MVector with a new one, you can replace the values inside it with Bx[i].d .= rand(3,3)*rand(3) (notice the dot before the equal sign).
  3. If your struct is not mutable neither the type of the matrix inside it then you cannot modify its value after creation and must create a new object to pass around each time you need the value to be changed.
1 Like

I am sorry, it is a typo. I have corrected it to be SVector

Thank you very much for your explanation!
1- What does replacing the whole array in a swoop mean?
2- Can I do Bx[i].d = rand(3,3)*rand(3) on the below two structure since they are mutable?

About (1), I mean that you cannot replace the array inside the struct by an entirely new array, the array used in the construction of the struct will always be the same, the only thing that can be changed is its contents.

julia> struct A; a :: Vector{Int}; end

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

julia> a_var = A(x)
A([1, 2, 3])

julia> a_var.a = [4, 5, 6]
ERROR: setfield! immutable struct of type A cannot be changed
Stacktrace:
 [1] setproperty!(::A, ::Symbol, ::Array{Int64,1}) at ./Base.jl:34
 [2] top-level scope at REPL[4]:1

julia> x
3-element Array{Int64,1}:
 1
 2
 3

julia> a_var.a .= [4, 5, 6]
3-element Array{Int64,1}:
 4
 5
 6

julia> x
3-element Array{Int64,1}:
 4
 5
 6

julia> mutable struct B; b :: Vector{Int}; end

julia> y = [1, 2, 3]
3-element Array{Int64,1}:
 1
 2
 3

julia> b_var = B(y)
B([1, 2, 3])

julia> b_var.b = [4, 5, 6]
3-element Array{Int64,1}:
 4
 5
 6

julia> y
3-element Array{Int64,1}:
 1
 2
 3

julia> b_var.b
3-element Array{Int64,1}:
 4
 5
 6

julia> b_var.b
3-element Array{Int64,1}:
 4
 5
 6

  1. Yes, you can.

I fell like the answers to some of your questions could be obtained by just running your code through the REPL.

1 Like

Thank you for your explanation!
Yes, I tried to run the code in the REPL but I didnt understand the exact difference such as in the below code.
1- The field d is static in B3, why is it still possible to change it by an entirely new array?
2- Could you please give me an example to show the unique usage for B3 and B4 (in terms of replacing/filling the field d)?
3- How can I solve the below error?

julia> mutable struct B4
           d::MVector{3,Float64}
end
julia> mutable struct B3
             d::SVector{3,Float64}
        end

julia> a_var = B3([1,2,3])
B3([1.0, 2.0, 3.0])

julia> a_var.d = [4,5,6]
3-element Vector{Int64}:
 4
 5
julia> a_var.d[1:2] = [4,5]
ERROR: setindex!(::SVector{3, Float64}, value, ::Int) is not defined.
Stacktrace:
julia> a_var.d[1:2] .= [4,5]
ERROR: setindex!(::SVector{3, Float64}, value, ::Int) is not defined.
Stacktrace:
  1. Yes. You cannot replace values inside d because the SArray object is immutable. However, the B3 object is mutable, and therefore a new object may be assigned to field d.
  2. What do you mean by unique usage? I do not undertsand the question.
  3. You cannot “solve” the error. It is a consequence of you using a SArray (i.e., an immutable object), you cannot mutate an SArray. The best you can do is to create a new array with the changed values and assign it. a_var.d = [4, 5, a_var.d[3]]
1 Like

Thanks for your helpful response.
What is the difference between mutate, = assignment .= assignment in julia?

mutate does not exist, not as a function, at least. Mutation happens when you use = but the left-hand side is not just a variable name but a field inside an object (or a position inside an array). For example, a = 10 is a binding that binds the name a to the value 10 (i.e., not mutation) but a.my_field = 10 is an assignment of the value 10 to the field my_field of object a (i.e., a mutation). a = 10 is a language primitive, a.my_field = 10 calls Base.setproperty!(a, :my_field, 10) and its meaning can be changed by adding methods to this function. Finally, .= is another completely distinct thing, it can never be used for binding (i.e., to create a new variable) it only works on already existing objects/variables that are containers and it mutates each element of the container in the left-hand side to the value of the elements on the container of the right-hand side. You can use ?.= in the REPL to check its documentation, it is replaced by Julia to a call to broadcast!(identity, left_hand_variable, right_hand_variable).

In other words, things that look similar are completely different things, in fact.

2 Likes

See: Assignment and mutation · JuliaNotes.jl

It compiles other previous answers to that.

1 Like

Thank you very much for explanation. I know that in Julia a scalar is immutable. However, if I have an immutable structure B3, is there any way (a trick) to change its scalar property s to be mutable, i.e. B3 remains immutable?

struct B3
  d::MVector{3,Float64}
  s::Int64
end
a_var = B3([1,2,3],0)
a_var.d[1:3] = [6,6,6]
a_var.s = Int(4)
ERROR: setfield!: immutable struct of type B3 cannot be changed

Not really.

You can change field s to be of type Base.RefValue{Int64} but this needs the struct definition to be different and you will need to use [] every time you want to access the value, as in:

julia> struct B3
         s::Base.RefValue{Int64}
       end

julia> b3 = B3(Ref(1))
B3(Base.RefValue{Int64}(1))

julia> b3.s[]
1

julia> b3.s[] = 2
2

julia> b3
B3(Base.RefValue{Int64}(2))

What you will probably want is to rebuild the object with everything the same except the changed value.

julia> struct B3
         d::Vector{Float64}
         s::Int64
       end

julia> b3 = B3([1, 2, 3], 10)
B3([1.0, 2.0, 3.0], 10)

julia> b3 = B3(b3.d, 11)
B3([1.0, 2.0, 3.0], 11)

There is a library called Setfield.jl that make it easier to re-create an object changing just a single field, but note this is not the same as changing the original.

1 Like

Thank you very much for your very help feedbacks!