Say I have a struct:
mutable struct DataHolder
data1::Vector{Float64}
data2::Vector{Float64}
function DataHolder()
emp = Float64[]
new(emp, emp)
end
end
d = DataHolder()
When I try to push a value to only one element of struct d by doing:
push!(d.data1, 1.0)
the value is pushed not only d.data1 but also d.data2. Indeed, the REPL says
julia> d
DataHolder([1.0], [1.0])
How can I push a value to only one element of the struct??
Your problem is not in push!, but rather in the inner constructor of DataHolder. Specifically:
emp = Float64[]
new(emp, emp)
This code pattern means that the fields of the new DataHolder both point toward the same array (in memory). So if you mutate one of them (say, via push!), you also mutate the other.
You could instead replace those two lines with:
new(Float64[], Float64[])
to get the behaviour you want.
More generally, although it is not forbidden, your use of the inner constructor is a bit odd. Typically, inner constructors should have a method signature corresponding exactly to the fields of your struct, and the inner constructor itself is typically only used to provide a set of universal tests that any new DataHolder should undergo.
Personally I would re-write your code as follows:
mutable struct DataHolder
data1::Vector{Float64}
data2::Vector{Float64}
function DataHolder(data1::Vector{Float64}, data2::Vector{Float64})
#Any tests on data1 and data2 go here
new(data1, data2)
end
end
DataHolder() = DataHolder(Float64[], Float64[])
If you don't need to do any universal tests on DataHolder, then delete the inner constructor entirely.
Final food for thought: Does DataHolder really need to be mutable? If you only want to be able to mutate the arrays in data1 and data2, then DataHolder itself does not need to be mutable, because those arrays are already mutable. You only need DataHolder to be mutable if you plan to completely re-allocate the values in those fields, e.g. an operation of the form dh.data1 = [2.0].
UPDATE AFTER COMMENT:
Personally I don't see anything wrong with DataHolder() = DataHolder(Float64[], ..., Float64[]). It's just one line of code and you never have to think about it again. Alternatively, you could do:
DataHolder() = DataHolder([ Float64[] for n = 1:10 ]...)
which just splats an a vector of empty vectors into the constructor arguments.
#ColinTBowers has answered your question. Here is a very simple and a more general implementation:
struct DataHolder # add mutable if you really need it
data1::Vector{Float64}
data2::Vector{Float64}
end
DataHolder() = DataHolder([], [])
Perhaps you'd like to allow other types than Float64 (because why wouldn't you?!):
struct DataHolder{T}
data1::Vector{T}
data2::Vector{T}
end
DataHolder{T}() where {T} = DataHolder{T}([], [])
DataHolder() = DataHolder{Float64}() # now `Float64` is the default type.
Now you can do this:
julia> DataHolder{Rational}()
DataHold{Rational}(Rational[], Rational[])
julia> DataHolder()
DataHold{Float64}(Float64[], Float64[])
Related
Here's a psuedocode implementation of what I would be looking for within Julia:
struct Example
field1::Float64
field2::Float64
end # End struct
example = Example(1., 2.)
function modifystruct(mystruct, fieldname)
mystruct.fieldname +=10
return mystruct
end
modifystruct(example, field1)
# In this instance I would want to have example.field1 = 11.
How would I actually do this? I want to provide the fieldname as something like a string, and have my struct."whateverfieldname" get modified as such. I should add that I do NOT want to code something in like this:
function modifystruct(mystruct, fieldname)
if fieldname = "fieldname1"
mystruct.field1 +=10
end
if fieldname = "fieldname2"
mystruct.field2 +=10
end
return mystruct
end
Largely due to how versatile I want this code to be. I may be using different types of structs for my program, so the closest I can get to directly accessing by the name of the field, the better. Is there any method or implementation that can do this for me?
Sure, that's setproperty!(value, name, x) and getproperty(value, name):
function modifystruct(mystruct, fieldname)
new_field = getproperty(mystruct, fieldname) + 10
setproperty!(mystruct, fieldname, new_field)
return mystruct
end
As DecowVR rightly notes, this requires mystruct to be mutable.
If you want to do this repeatedly and with nested properties, you might be interested in lenses such as those provided by Setfield.jl.
Firstly, whould be noticed that in order to be able to modify an struct, it needs to be mutable:
julia> mutable struct Example
field1::Float64
field2::Float64
end
julia> example = Example(1., 2.)
Example(1.0, 2.0)
And now, a simple aproach would be to use Julia Symbols. A symbol is nothing else but an expression like :var. Can be used as shown:
julia> example.:field1
1.0
However, if we create a variable that stores the symbol, it won't work:
julia> v = :field1
:field1
julia> example.v
ERROR: type Example has no field v
Stacktrace:
[1] getproperty(x::Example, f::Symbol)
# Base ./Base.jl:42
[2] top-level scope
# REPL[18]:1
This is due to the order in which the Julia Interpreter works. If we want to evaluate firstly the variable, and then the expression, it is as easy as:
julia> #eval example.$v
1.0
So the complete function would be as follows:
julia> function modify_struct(mystruct::Example, fieldname::Symbol)
#eval $mystruct.$fieldname += 10
end
Can someone explain why NamedTuples and immutable structs are separate instead of NamedTuples being an anonymous struct like there are anonymous functions function (x) x^2 end? They look like they have the same structure conceptually (I would also like to know if they have a different memory layout), though they have different methods to access their fields (example below). It seems very plausible to implement the NamedTuple methods for structs, but I may just not be aware of a good reason not to do that.
struct X; a; b; c; end
Xnt = NamedTuple{(:a,:b,:c), Tuple{Any, Any, Any}}
t1 = (10, 20.2, 30im)
#
#
# t1[1] indexing by position
# t1[1:2] slicing
# for el in t1 iteration
x1 = X(t1...)
# x1.a getting field
xnt1 = Xnt(t1)
# xnt1.a getting field
# xnt1[:a] indexing by field
# xnt1[1] indexing by position
#
# for el in t1 iteration
Every single NamedTuple instance with the same names and field types is of the same type. Different structs (types) can have the same number and type of fields but are different types.
A named tuple has names for each column in the tuple. Named tuples are an alternative to a dataframe. When instantiating the namedTuple, you pass the list of field names as a list. Named tuples create a specification contract for expected fields and reduce the chance of code breaking.
I have been writing stochastic PDE simulations in Julia, and as my problems have become more complicated, the number of independent parameters has increased. So what starts with,
myfun(N,M,dt,dx,a,b)
eventually becomes
myfun(N,M,dt,dx,a,b,c,d,e,f,g,h)
and it results in (1) messy code, (2) increased chance of error due to misplaced function arguments, (3) inability to generalise for use in other functions.
(3) is important, because I have made simple parallelisation of my code to evaluate many different runs of the PDEs. So I would like to convert my functions into a form:
myfun(args)
where args contains all the relevant arguments. The problem I am finding with Julia, is that creating a struct containing all my relevant parameters as attributes slows things down considerably. I think this is due to the continual accessing of the struct attributes. As a simple (ODE) working example,
function example_fun(N,dt,a,b)
V = zeros(N+1)
U = 0
z = randn(N+1)
for i=2:N+1
V[i] = V[i-1]*(1-dt)+U*dt
U = U*(1-dt/a)+b*sqrt(2*dt/a)*z[i]
end
return V
end
If I try to rewrite this as,
function example_fun2(args)
V = zeros(args.N+1)
U = 0
z = randn(args.N+1)
for i=2:args.N+1
V[i] = V[i-1]*(1-args.dt)+U*args.dt
U = U*(1-args.dt/args.a)+args.b*sqrt(2*args.dt/args.a)*z[i]
end
return V
end
Then while the function call looks elegant, it is cumbersome to rework accessing every attribute from the class and also this continual accessing of attributes slows the simulation down. What is a better solution? Is there a way to simply 'unpack' the attributes of a struct so they do not have to be continually accessed? And if so, how would this be generalised?
edit:
I am defining the struct I use as follows:
struct Args
N::Int64
dt::Float64
a::Float64
b::Float64
end
edit2: I have realised that structs with Array{} attributes can give rise to a performance difference if you do not specify the dimensions of the array in the struct definition. For example, if c is a one-dimensional array of parameters,
struct Args_1
N::Int64
c::Array{Float64}
end
will give far worse performance in f(args) than f(N,c). However, if we specify that c is a one-dimensional array in the struct definition,
struct Args_1
N::Int64
c::Array{Float64,1}
end
then the performance penalty disappears. This issue and the type instability shown in my function definitions seem to account for the performance difference I encountered when using a struct as the function argument.
In your code there is a type instability, related to U which is initialized as an 0 (integer), but if you replace it with 0. (floating point number), the type-instability disapears.
For the original versions (with "U=0"), function example_fun takes 801.933 ns (for the parameters 10,0.1,2.,3.) and example_fun2 925.323 ns (for similar values).
In the type-stable version (U=0.), both take 273 ns (+/5 ns). Thus this a substantial speed-up and there is no more a penalty of combining the arguments in the type args.
Here is the complete function:
function example_fun2(args)
V = zeros(args.N+1)
U = 0.
z = randn(args.N+1)
for i=2:args.N+1
V[i] = V[i-1]*(1-args.dt)+U*args.dt
U = U*(1-args.dt/args.a)+args.b*sqrt(2*args.dt/args.a)*z[i]
end
return V
end
Maybe you did not declare the types of the parameters of the type declaration of args?
Consider this small example:
struct argstype
N
dt
end
myfun(args) = args.N * args.dt
myfun is not type-stable can the type of the return type cannot be inferred:
#code_warntype myfun(argstype(10,0.1))
Variables:
#self# <optimized out>
args::argstype
Body:
begin
return ((Core.getfield)(args::argstype, :N)::Any * (Core.getfield)(args::argstype, :dt)::Any)::Any
end::Any
However, if you declare the types, then code becomes type-stable:
struct argstype2
N::Int
dt::Float64
end
#code_warntype myfun(argstype2(10,0.1))
Variables:
#self# <optimized out>
args::argstype2
Body:
begin
return (Base.mul_float)((Base.sitofp)(Float64, (Core.getfield)(args::argstype2, :N)::Int64)::Float64, (Core.getfield)(args::argstype2, :dt)::Float64)::Float64
end::Float64
You see that the inferred return type of Float64.
With parametric types (https://docs.julialang.org/en/v0.6.3/manual/types/#Parametric-Types-1), your code still remains generic and type-stable at the same time:
struct argstype3{T1,T2}
N::T1
dt::T2
end
#code_warntype myfun(argstype3(10,0.1))
Variables:
#self# <optimized out>
args::argstype3{Int64,Float64}
Body:
begin
return (Base.mul_float)((Base.sitofp)(Float64, (Core.getfield)(args::argstype3{Int64,Float64}, :N)::Int64)::Float64, (Core.getfield)(args::argstype3{Int64,Float64}, :dt)::Float64)::Float64
end::Float64
I have an immutable structure with four objects defined as follows:
struct FltFric
muS::Array{Float64, 2}
muD::Array{Float64, 2}
Dc::Float64
W::Array{Float64, 2}
end
muS = repmat([0.6], 100, 1) # Coefficient of static friction
muD = repmat([0.5], 100, 1) # Coefficient of dynamic friction
Dc = 0.1 # Critical slip distance
FltFriction = FltFric(muS, muD, Dc, zeros(size(muS)))
I am modifying the values of FltFric.muS as follows:
FltFriction.muS[1:20] = 100
This works fine. But when I try to modify the value of W
FltFriction.W = (FltFriction.muS - FltFriction.muD)./(FltFriction.Dc)
This gives me an error: type FltFric is immutable.
Why does the first statement not give error while the second one does? If the type is immutable, both statements should give an error. What is the difference between the two assignments?
I know that I can circumvent the problem by typing mutable struct, but I don't understand the difference in my two assignments.
I am not a Julia expert, but I think this is a more general question.
In the first assignment, you're modifying certain elements of the list FltFriction.muS. This is fine since although the struct is immutable, the list referred to by .muS is mutable. In other words, you're mutating the list by changing its elements, rather than mutating the struct.
In the second assignment, you're trying to replace the entire list .W in one fell swoop. In this case you're trying to mutate the struct directly, replacing one of its elements. For this reason, the second assignment fails while the first one succeeds.
I'm speculating here, but I suspect that if you tried to do the second assignment like so:
FltFriction.W[1:end] = ...
Then you would be fine, since you're mutating the list instead of the struct.
As pointed out by a commenter (see below), in Julia there is a "more idiomatic (and more performant)" way to do this correctly and without mutating the struct itself by using the in-place assignment operator (neat!):
FltFriction.W .= (FltFriction.muS - FltFriction.muD)./FltFriction.Dc
I couldn't find an answer in both stackoverflow and the Julia docs to the following "design problem":
Let's say I want to define the following object
struct Person
birthplace::String
age::Int
end
Since Person is immutable, I'm happy that nobody can change the birthplace of any Person created, nonetheless, this also implies that when time passes, I cannot change their age either...
On the other hand, if I define the type Person as
mutable struct Person
birthplace::String
age::Int
end
I can now make them age, but I don't have the safety I had before on the birthplace, anyone can access it and change it.
The workaround I found so far is the following
struct Person
birthplace::String
age::Vector{Int}
end
where obviously age is a 1-element Vector.
I find this solution quite ugly and definitely suboptimal as I have to access the age with the square brackets every time.
Is there any other, more elegant, way to have both immutable and mutable fields in an object?
Maybe the problem is that I am missing the true value of having either everything mutable or immutable within a struct. If that's the case, could you explain me that?
For this particular example it seems better to store the birthdate rather than the age, since the birthdate is also immutable, and it is simple enough to calculate the age from that information, but perhaps this is just a toy example.
I find this solution quite ugly and definitely suboptimal as I have to
access the age with the square brackets every time.
Usually you would define a getter, i.e. something like age(p::Person) = p.age[1] that you use instead of accessing the field directly. With this you avoid the "ugliness" with the brackets.
In this case, where we only want to store a single value, it is also possible to use a Ref (or possibly a 0-dimensional Array), something like:
struct Person
birthplace::String
age::Base.RefValue{Int}
end
Person(b::String, age::Int) = Person(b, Ref(age))
age(p::Person) = p.age[]
with usage:
julia> p = Person("earth", 20)
Person("earth", 20)
julia> age(p)
20
You've received some interesting answers, and for the "toy example" case, I like the solution of storing the birth-date. But for more general cases, I can think of another approach that might be useful. Define Age as its own mutable struct, and Person as an immutable struct. That is:
julia> mutable struct Age ; age::Int ; end
julia> struct Person ; birthplace::String ; age::Age ; end
julia> x = Person("Sydney", Age(10))
Person("Sydney", Age(10))
julia> x.age.age = 11
11
julia> x
Person("Sydney", Age(11))
julia> x.birthplace = "Melbourne"
ERROR: type Person is immutable
julia> x.age = Age(12)
ERROR: type Person is immutable
Note that I can't alter either field of Person, but I can alter the age by directly accessing the age field in the mutable struct Age. You could define an accessor function for this, ie:
set_age!(x::Person, newage::Int) = (x.age.age = newage)
julia> set_age!(x, 12)
12
julia> x
Person("Sydney", Age(12))
There is nothing wrong with the Vector solution discussed in another answer. It is essentially accomplishing the same thing, since array elements are mutable. But I think the above solution is neater.
In Julia 1.8, you can use
mutable struct Person
age::Int
const birthplace::String
end
Cf. https://docs.julialang.org/en/v1.8-dev/manual/types/#Composite-Types