Meaning of a immutable name in julia package - julia

I just downloaded the package RandomMatrices.jl to julia because I need to compute the Tracy-Widom densities. However the docs does not help me much in understanding what does it mean:
immutable TracyWidom <: ContinuousUnivariateDistribution
end
What kind of type is this TracyWidom name? What should I supply in order to do some tests with the function
pdf(d::TracyWidom, t::Real)
contained in TracyWidom.jl?

The package defines a TracyWidom type, which has no fields. You can create a new instance of the TracyWidom type by typing TracyWidom().
To calculate the PDF of the TracyWidom distribution, use pdf(TracyWidom(), 0.5).
You can figure this out from the pdf type signature: the first argument should be an object of type TracyWidom, and the second of type Real.
Julia has a rich type system, but relevant for here are the following: abstract, which can't be created, immutable, which can't be modified after being created, and type which is the "normal" kind.
In Distributions.jl, which this is based off, there are types defined for each distribution, e.g. the normal distribution is
immutable Normal <: ContinuousUnivariateDistribution
μ::Float64
σ::Float64
# Some other stuff...
end
which makes more sense because its defined by those two parameters - TracyWidom doesn't need any parameters, but we still follow the same style. The <: ContinuousUnivariateDistribution means that Normal and TracyWidom are both ContinuousUnivariateDistributions, which is an abstract type.

Related

The Idiomatic Way To Do OOP, Types and Methods in Julia

As I'm learning Julia, I am wondering how to properly do things I might have done in Python, Java or C++ before. For example, previously I might have used an abstract base class (or interface) to define a family of models through classes. Each class might then have a method like calculate. So to call it I might have model.calculate(), where the model is an object from one of the inheriting classes.
I get that Julia uses multiple dispatch to overload functions with different signatures such as calculate(model). The question I have is how to create different models. Do I use the type system for that and create different types like:
abstract type Model end
type BlackScholes <: Model end
type Heston <: Model end
where BlackScholes and Heston are different types of model? If so, then I can overload different calculate methods:
function calculate(model::BlackScholes)
# code
end
function calculate(model::Heston)
# code
end
But I'm not sure if this is a proper and idiomatic use of types in Julia. I will greatly appreciate your guidance!
This is a hard question to answer. Julia offers a wide range of tools to solve any given problem, and it would be hard for even a core developer of the language to assert that one particular approach is "right" or even "idiomatic".
For example, in the realm of simulating and solving stochastic differential equations, you could look at the approach taken by Chris Rackauckas (and many others) in the suite of packages under the JuliaDiffEq umbrella. However, many of these people are extremely experienced Julia coders, and what they do may be somewhat out of reach for less experienced Julia coders who just want to model something in a manner that is reasonably sensible and attainable for a mere mortal.
It is is possible that the only "right" answer to this question is to direct users to the Performance Tips section of the docs, and then assert that as long as you aren't violating any of the recommendations there, then what you are doing is probably okay.
I think the best way I can answer this question from my own personal experience is to provide an example of how I (a mere mortal) would approach the problem of simulating different Ito processes. It is actually not too far off what you have put in the question, although with one additional layer. To be clear, I make no claim that this is the "right" way to do things, merely that it is one approach that utilizes multiple dispatch and Julia's type system in a reasonably sensible fashion.
I start off with an abstract type, for nesting specific subtypes that represent specific models.
abstract type ItoProcess ; end
Now I define some specific model subtypes, e.g.
struct GeometricBrownianMotion <: ItoProcess
mu::Float64
sigma::Float64
end
struct Heston <: ItoProcess
mu::Float64
kappa::Float64
theta::Float64
xi::Float64
end
Note, in this case I don't need to add constructors that convert arguments to Float64, since Julia does this automatically, e.g. GeometricBrownianMotion(1, 2.0) will work out-of-the-box, as Julia will automatically convert 1 to 1.0 when constructing the type.
However, I might want to add some constructors for common parameterizations, e.g.
GeometricBrownianMotion() = GeometricBrownianMotion(0.0, 1.0)
I might also want some functions that return useful information about my models, e.g.
number_parameter(model::GeometricBrownianMotion) = 2
number_parameter(model::Heston) = 4
In fact, given how I've defined the models above, I could actually be a bit sneaky and define a method that works for all subtypes:
number_parameter(model::T) where {T<:ItoProcess} = length(fieldnames(typeof(model)))
Now I want to add some code that allows me to simulate my models:
function simulate(model::T, numobs::Int, stval) where {T<:ItoProcess}
# code here that is common to all subtypes of ItoProcess
simulate_inner(model, somethingelse)
# maybe more code that is common to all subtypes of ItoProcess
end
function simulate_inner(model::GeometricBrownianMotion, somethingelse)
# code here that is specific to GeometricBrownianMotion
end
function simulate_inner(model::Heston, somethingelse)
# code here that is specific to Heston
end
Note that I have used the abstract type to allow me to group all code that is common to all subtypes of ItoProcess in the simulate function. I then use multiple dispatch and simulate_inner to run any code that needs to be specific to a particular subtype of ItoProcess. For the aforementioned reasons, I hesitate to use the phrase "idiomatic", but let me instead say that the above is quite a common pattern in typical Julia code.
The one thing to be careful of in the above code is to ensure that the output type of the simulate function is type-stable, that is, the output type can be uniquely determined by the input types. Type stability is usually an important factor in ensuring performant Julia code. An easy way in this case to ensure type-stability is to always return Matrix{Float64} (if the output type is fixed for all subtypes of ItoProcess then obviously it is uniquely determined). I examine a case where the output type depends on input types below for my estimate example. Anyway, for simulate I might always return Matrix{Float64} since for GeometricBrownianMotion I only need one column, but for Heston I will need two (the first for price of the asset, the second for the volatility process).
In fact, depending on how the code is used, type-stability is not always necessary for performant code (see eg using function barriers to prevent type-instability from flowing through to other parts of your program), but it is a good habit to be in (for Julia code).
I might also want routines to estimate these models. Again, I can follow the same approach (but with a small twist):
function estimate(modeltype::Type{T}, data)::T where {T<:ItoProcess}
# again, code common to all subtypes of ItoProcess
estimate_inner(modeltype, data)
# more common code
return T(some stuff generated from function that can be used to construct T)
end
function estimate_inner(modeltype::Type{GeometricBrownianMotion}, data)
# code specific to GeometricBrownianMotion
end
function estimate_inner(modeltype::Type{Heston}, data)
# code specific to Heston
end
There are a few differences from the simulate case. Instead of inputting an instance of GeometricBrownianMotion or Heston, I instead input the type itself. This is because I don't actually need an instance of the type with defined values for the fields. In fact, the values of those fields is the very thing I am attempting to estimate! But I still want to use multiple dispatch, hence the ::Type{T} construct. Note also I have specified an output type for estimate. This output type is dependent on the ::Type{T} input, and so the function is type-stable (output type can be uniquely determined by input types). But common with the simulate case, I have structured the code so that code that is common to all subtypes of ItoProcess only needs to be written once, and code that is specific to the subtypes is separted out.
This answer is turning into an essay, so I should tie it off here. Hopefully this is useful to the OP, as well as anyone else getting into Julia. I just want to finish by emphasizing that what I have done above is only one approach, there are others that will be just as performant, but I have personally found the above to be useful from a structural perspective, as well as reasonably common across the Julia ecosystem.

Julia: write a union of types using "abstract type"

I am trying to write the following union of types:
FloatVector = Union{Array{Float64,1}, DataArray{Float64,1}, Array{Union{Float64, Missings.Missing},1}};
using the abstract types syntax. Ideally, I would like to do something similar to this link. I tried the below, but unfortunately it does not work:
abstract type FloatVector end
type Array{Float64,1} <: FloatVector end
type DataArray{Float64,1} <: FloatVector end
type Array{Union{Float64, Missings.Missing},1} <: FloatVector end
I am not confident with abstract types and I couldn't find a good reference on a similar problem. I would be glad if you could explain me how to proceed and the advantages over the Union.
It is not possible to do what you want using abstract types in Julia. Union is a way to represent your requirement.
Now to understand why observe that in Julia type system you have three restrictions:
Only abstract types can have subtypes.
A type can have only one supertype.
You have to specify a supertype of a type when defining it.
In this way we know that types create a tree like structure, where the root is Any (with the exception of Union{} which has no values and is subtype of all other types, but you will probably not need this in practice).
Your definition would violate rule #2 and rule #3. Observe that all those types are already defined (either in Base or in a package) - so you cannot redefine them (rule #3). Also they already have a supertype so you cannot add another (rule #2). See for example the result of this call:
julia> supertype(Array{Float64, 1})
DenseArray{Float64,1}
And you see that Array{Float64, 1} is already defined and has a supertype.
Having said that maybe another more general definition will be just enough for you:
const FloatOrMissingVector = AbstractVector{<:Union{Float64, Missing}}
This definition has two differences:
It allows other than dense vectors (which I guess is what you would want anyway as typically you do not care if a vector is dense or sparse)
It would allow e.g. Vector{Missing} as a subtype (so a structure containing only missing values) - if you would want to rule this out you would have to specify union as you have proposed above.

Implementing custom primitive types in Julia

The Julia documentation says:
A primitive type is a concrete type whose data consists of plain old
bits. Classic examples of primitive types are integers and
floating-point values. Unlike most languages, Julia lets you declare
your own primitive types, rather than providing only a fixed set of
built-in ones. In fact, the standard primitive types are all defined
in the language itself:
I'm unable to find an example of how to do this, though, either in the docs or in the source code or anywhere else. What I'm looking for is an example of how to declare a primitive type, and how to subsequently implement a function or method on that type that works by manipulating those bits.
Is anyone able to point me towards an example? Thanks.
Edit: It's clear how to declare a primitive type, as there are examples immediately below the above quote in the doc. I'm hoping for information about how to subsequently manipulate them. For example, say I wanted to (pointlessly) implement my own primitive type MyInt8. I could declare that with primitive type MyInt8 <: Signed 8 end. But how would I subsequently implement a function myplus that manipulated the bits within Myint8?
PS in case it helps, the reason I'm asking is not because I need to do anything specific in Julia; I'm designing my own language for fun, and am researching how other languages implement various things.
# Declare the new type.
primitive type MyInt8 <: Signed 8 end
# A constructor to create values of the type MyInt8.
MyInt8(x :: Int8) = reinterpret(MyInt8, x)
# A constructor to convert back.
Int8(x :: MyInt8) = reinterpret(Int8, x)
# This allows the REPL to show values of type MyInt8.
Base.show(io :: IO, x :: MyInt8) = print(io, Int8(x))
# Declare an operator for the new type.
import Base: +
+ (a :: MyInt8, b :: MyInt8) = MyInt8(Int8(a) + Int8(b))
The key function here is reinterpret. It allows the bit representation of an Int8 to be treated as the new type.
To store a value with a custom bit layout, inside the MyInt8 constructor, you could perform any of the standard bit manipulation functions on the Int8 before 'reinterpreting' them as a MyInt8.

Function of parameter type in type definition

Assume I want to store I vector together with its norm. I expected the corresponding type definition to be straightforward:
immutable VectorWithNorm1{Vec <: AbstractVector}
vec::Vec
norm::eltype(Vec)
end
However, this doesn't work as intended:
julia> fieldtype(VectorWithNorm1{Vector{Float64}},:norm)
Any
It seems I have to do
immutable VectorWithNorm2{Vec <: AbstractVector, Eltype}
vec::Vec
norm::Eltype
end
and rely on the user to not abuse the Eltype parameter. Is this correct?
PS: This is just a made-up example to illustrate the problem. It is not the actual problem I'm facing.
Any calculations on a type parameter currently do not work
(although I did discuss the issue with Jeff Bezanson at JuliaCon, and he seemed amenable to fixing it).
The problem currently is that the expression for the type of norm gets evaluated when the parameterized type is defined, and gets called with a TypeVar, but it is not yet bound to a value, which is what you really need it to be called with, at the time that that parameter is actually bound to create a concrete type.
I've run into this a lot, where I want to do some calculation on the number of bits of a floating point type, i.e. to calculate and use the number of UInts needed to store a fp value of a particular precision, and use an NTuple{N,UInt} to hold the mantissa.

How to get a function from a symbol without using eval?

I've got a symbol that represents the name of a function to be called:
julia> func_sym = :tanh
I can use that symbol to get the tanh function and call it using:
julia> eval(func_sym)(2)
0.9640275800758169
But I'd rather avoid the 'eval' there as it will be called many times and it's expensive (and func_sym can have several different values depending on context).
IIRC in Ruby you can say something like:
obj.send(func_sym, args)
Is there something similar in Julia?
EDIT: some more details on why I have functions represented by symbols:
I have a type (from a neural network) that includes the activation function, originally I included it as a funcion:
type NeuralLayer
weights::Matrix{Float32}
biases::Vector{Float32}
a_func::Function
end
However, I needed to serialize these things to files using JLD, but it's not possible to serialize a Function, so I went with a symbol:
type NeuralLayer
weights::Matrix{Float32}
biases::Vector{Float32}
a_func::Symbol
end
And currently I use the eval approach above to call the activation function. There are collections of NeuralLayers and each can have it's own activation function.
#Isaiah's answer is spot-on; perhaps even more-so after the edit to the original question. To elaborate and make this more specific to your case: I'd change your NeuralLayer type to be parametric:
type NeuralLayer{func_type}
weights::Matrix{Float32}
biases::Vector{Float32}
end
Since func_type doesn't appear in the types of the fields, the constructor will require you to explicitly specify it: layer = NeuralLayer{:excitatory}(w, b). One restriction here is that you cannot modify a type parameter.
Now, func_type could be a symbol (like you're doing now) or it could be a more functionally relevant parameter (or parameters) that tunes your activation function. Then you define your activation functions like this:
# If you define your NeuralLayer with just one parameter:
activation(layer::NeuralLayer{:inhibitory}) = …
activation(layer::NeuralLayer{:excitatory}) = …
# Or if you want to use several physiological parameters instead:
activation{g_K,g_Na,g_l}(layer::NeuralLayer{g_K,g_Na,g_l} = f(g_K, g_Na, g_l)
The key point is that functions and behavior are external to the data. Use type definitions and abstract type hierarchies to define behavior, as is coded in the external functions… but only store data itself in the types. This is dramatically different from Python or other strongly object-oriented paradigms, and it takes some getting used to.
But I'd rather avoid the 'eval' there as it will be called many times and it's expensive (and func_sym can have several different values depending on context).
This sort of dynamic dispatch is possible in Julia, but not recommended. Changing the value of 'func_sym' based on context defeats type inference as well as method specialization and inlining. Instead, the recommended approach is to use multiple dispatch, as detailed in the Methods section of the manual.

Resources