Julia: function inside a struct and method constructor - struct

What is the correct way to store a function in a struct constructor? And retrieve it? And change its parameters?
I wrote some code back in version 0.4 to 0.5, which unsurprisingly no longer works. In words: I have a struct-cum-method named Model in which I define parameters and functional forms. And a similarly structured Solution in which I solve instances of the model. My purpose is to make multiple simulations of the model for different sets of parameters and functional forms. The code below is part of a module inside a package. I don't have a good handle on how to deal with the function type in Julia 1.6 (something like the code below used to work).
# Non-Parametric version of struct + outer method constructor
# Model parameters and functions
struct Model
f::Function
p::Float64
n::Int64
end
function Model(;
f::Function = x -> x + p,
p::Float64 = 2.0,
n::Int64 = 4
)
Model(f, p, n)
end
This is the output of Model():
julia> m = Model()
Model(var"#2#4"(), 2.0, 4)
julia> Model(p = 1.0)
Model(var"#3#5"(), 1.0, 4)
julia> m.f
#2 (generic function with 1 method)
julia> m.p
2.0
julia> m.n
4
julia> Model(f = x -> x - p)
Model(var"#15#16"(), 2.0, 4)
julia> m.f(1.0)
ERROR: UndefVarError: p not defined
Stacktrace:
[1] (::var"#2#4")(x::Float64)
# Main ./REPL[2]:2
[2] top-level scope
# REPL[4]:1
I'd be grateful for pointers on where the code goes wrong and how to fix it. If at all recommended, I'd like a parametric struct.

You need to use Base.#kwdef or an #with_kw equivalent from the Parameters package (I prefer the latter since it provides nicer formatting for the console)
Base.#kwdef struct Model
p::Float64 = 2.0
n::Int64 = 4
f::Function = x -> x + p
end
This now can be used such as:
julia> Model(p=7).f(11)
18
Regarding solution you should separate the actual solution structure from the constructor. Hence you will have a function solve(m::Model) (or solve! if m is mutated in the process) that should yields a Solution object.
For creating the Solution object use the same pattern as above.

Related

Can I access a struct by name, eg A = field1, get struct.A?

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

Does value/reference types in Nim works same way as in Swift?

I'm trying to better understand reasoning behind the value types.
Does value/reference types in Nim works same way as in Swift?
yes, value and reference types do work in Nim as in Swift and other low-level programming languages like C#, C++, Rust. By that I mean that they follow these semantics with respect to copy:
Value semantics means that the copy owns its memory and lives separately from the copied.
Reference semantics means that the copy and the copied refer to the same underlying memory location.
(taken from this forum answer).
As an example, I translate this swift blog post to nim (playground):
# port of https://developer.apple.com/swift/blog/?id=10 to Nim
## Example of Value type
type S = object
data: int # I cannot say the default of data is -1. Nim does not have (yet) custom initialization, see accepted RFC https://github.com/nim-lang/RFCs/issues/252
var a = S()
var b = a
a.data = 42
echo (a, b) # ((data: 42), (data: 0))
## Example of Reference type
type C = ref object
data: int
func `$`(c: C): string = "C(data: " & $(c.data) & ")" # there is no default $ for ref objects as there is for objects
var x = C()
var y = x
x.data = 42
echo (x, y) # (C(data: 42), C(data: 42))
Note that:
Nim does not have a copy-on-write mechanism for value types as in Swift (but you can build your own CoW type)
Nim does not have a === operator like Swift (see this discussion).

Variable fieldnames in Julia mutable structs

I would like to able to get fields from a mutable struct in Julia using a variable.
e.g.
mutable struct myType
my_field1::Int = 1
my_field2::Int = 2
my_field3::Int = 3
end
And then let's imagine you declare a particular instance of this struct using struct_instance = myType()
How can you extract the value of a field from an instance of this mutable struct in a variable fashion?
Let's say you want to assign the value of my_struct.field[X] to a variable, using a for-loop, so that the particular field you're currently accessing depends on the variable X:
foo = zeros(Int64, 3)
for X = 1:3
foo(X) = struct_instance.field[X]
end
I don't know how to actually implement the above for-loop -- what I wrote above is just pseudo-code above. In MATLAB you would use the following notation, for instance:
foo = zeros(1,3)
for x = 1:3
foo(x) = struct_instance.(sprintf('field%d',x))
end
Thanks in advance.
For the code at the beginning of your example to work you need the Paramaters package, otherwise you are not able to have default values (the code from your example throws an error). I use it very often exactly in situations where I need a struct to use to represent a bunch of variables.
using Parameters
#with_kw mutable struct MyType
my_field1::Int = 1
my_field2::Int = 2
my_field3::Int = 3
end
This generates also a set of keyword methods that can be used to set the fields programmatically when creating the object. Have a look at the following code:
julia> vals = [Symbol("my_field$i") =>10i for i in 2:3 ]
2-element Array{Pair{Symbol,Int64},1}:
:my_field2 => 20
:my_field3 => 30
julia> MyType(;vals...)
MyType
my_field1: Int64 1
my_field2: Int64 20
my_field3: Int64 30
We have created here a set of field names and now we are using it when creating an object. This approach is particularly useful when you could consider using immutable object instead of mutable ones (immutable objects are always much faster).
You can mutate the object using setfield!:
julia> for i in 1:2
setfield!(m,Symbol("my_field$i"), 20i)
end
julia> m
MyType
my_field1: Int64 20
my_field2: Int64 40
my_field3: Int64 30
I think that when you are coming from Matlab this would be the most convenient way to layout your structs.
The function to get fields from a struct is fieldnames:
julia> mutable struct A
x
y
z
end
julia> fieldnames(A)
(:x, :y, :z)
To set those field values* programmatically, you can use setproperty!:
julia> a = A(1,1,1)
A(1,1,1)
julia> a.x
1
julia> setproperty!(a, :x, 2)
2
julia> a.x
2
With a for loop,
julia> for i in fieldnames(A)
setproperty!(a, i, 3)
end
julia> a
A(3,3,3)

Variable change in a function - Python 3

So I got the following code:
def foo(a, b, c):
try:
a = [0]
b[0] = 1
c[0] = 2
w[0] = 3
except:
pass
return z
x, y, z = [None], [None], [None]
w = z[:]
foo(x,y,z)
print(x,y,z,w)
The last line of the code print(x,y,z,w) prints [None] [1] [2] [3], however
I don't quite get it. Why are x,y,z are being changed from within the funciton? and if w changes - and it points to z, why doesnt z change accordingly?
In Python objects are passed by reference to functions.
This line makes a copy of z
w = z[:]
so changes to z don't affect w and vice versa. In the line
a = [0]
you change the reference to point to a new object, so you don't mutate x (which is what a was initially bound to). In the following lines
b[0] = 1
c[0] = 2
you mutate the objects that you got references to (y and z in global scope), so the objects in the outside scope change. In the line
w[0] = 3
you mutate the global object w since the name w is not a parameter of the function, nor is it bound in the body of the function.
What everyone else says is correct, but I want to add my way of thinking that may be helpful if you have experience with a language like C or C++.
Every variable in Python is a pointer (well, the technical term is a "reference", but I find that more difficult to visualize than "pointer"). You know how in C/C++ you can get a function to output multiple values by passing in pointers? Your code is doing essentially the same thing.
Of course, you may be wondering, if that is the case, why don't you see the same thing happening to ints, strs or whatnot? The reason is that those things are immutable, which means you cannot directly change the value of an int or a str at all. When you "change an integer", like i = 1, you are really changing the variable, pointing it to a different int object. Similarly, s += 'abc' creates a new str object with the value s + 'abc', then assigns it to s. (This is why s += 'abc' can be inefficient when s is long, compared to appending to a list!)
Notice that when you do a = [0], you are changing a in the second way --- changing the pointer instead of the object pointed to. This is why this line doesn't modify x.
Finally, as the others has said, w = z[:] makes a copy. This might be a little confusing, because for some other objects (like numpy arrays), this syntax makes a view instead of a copy, which means that it acts like the same object when it comes to changing elements. Remember that [] is just an operator, and every type of object can choose to give it a different semantical meaning. Just like % is mod for ints, and formatting for strs --- you sometimes just need to get familiar with the peculiarities of different types.

julia: efficient and tidy way to avoid many function arguments

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

Resources