Are the numerator and denominator stored as integers? Specifically, how are implemented the basic operations: sum, subtraction, multiplication, division?
https://github.com/JuliaLang/julia/blob/248bbf6d61b643d0101bf96093cd7621e5bcf477/base/rational.jl#L9-L15
the code is fairly readable, in short:
struct Rational{T<:Integer} <: Real
means that both numerator and denominator have the same type, and that type is <: Integer.
if you want to know how some operations are implemented, try running this in your REPL:
julia> #edit 1//2 + 3//4
it should bring you to https://github.com/JuliaLang/julia/blob/248bbf6d61b643d0101bf96093cd7621e5bcf477/base/rational.jl#L285
Even a faster and easier way to understand what is going on in Julia is just to use the dump command such as:
julia> dump(3//4)
Rational{Int64}
num: Int64 3
den: Int64 4
Related
I'm starting to learn Julia, and I was wondering if there is an equivalent function in Julia that is doing the same as the epsilon function in Fortran.
In Fortran, the epsilon function of variable x gives the smallest number of the same kind of x that satisfies 1+epsilon(x)>1
I thought the the function eps() in Julia would be something similar, so I tried
eps(typeof(x))
but I got the error:
MethodError: no method matching eps(::Type{Int64})
Is there any other function that resembles the Fortran one that can be used on the different variables of the code?
If you really need eps to work for both Float and Int types, you can overload a method of eps for Int by writing Base.eps(Int) = one(Int). Then your code will work. This is okay for personal projects, but not a good idea for code you intend to share.
As the docstring for eps says:
help?> eps
eps(::Type{T}) where T<:AbstractFloat
eps()
eps is only defined for subtypes of AbstractFloat i.e. floating point numbers. It seems that your variable x is an integer variable, as the error message says no method matching eps(::Type{Int64}). It doesn't really make sense to define an eps for integers since the "the smallest number of the same kind of x that satisfies 1 + epsilon(x) > 1" is always going to be 1 for integers.
If you did want to get a 1 of the specific type of integer you have, you can use the one function instead:
julia> x = UInt8(42)
0x2a
julia> one(typeof(x))
0x01
It seems like what you actually want is
eps(x)
not
eps(typeof(x))
The former is exactly equivalent to your Fortran function.
I am currently working on a parser which supports binary operations. The idea for representing a binary operation is to use a structure of this sort
struct Binary{T <: BinaryKind} <: Operation
xs::Vector{Union{Operation, Atom}}
end
Where both Operation and Atom are abstract types. After reading the performance tips in the julia documentation, I have come to realize that a more efficient way of representing this structure would be
struct Binary{T <: BinaryKind, D <: Tuple} <: Operation
xs::D
end
But Since I can have nested binary operations, I believe that there could be cases in which I would end up with very long type definitions, which could be even worse than just using an abstract type. Is there a way I can improve this?
I think (too long for the comment so I give it as an answer) that in this case it is probably better to be type unstable. Note that this is exactly what Julia does itself:
julia> x = :(1+2*(3+4))
:(1 + 2 * (3 + 4))
julia> dump(x)
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol *
2: Int64 2
3: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 3
3: Int64 4
Of course Julia has a significantly more rich syntax but even in your simple case consider the following. You get the benefits of type stability if you compile some part of code once and then run it many times (in this or other form).
Now I assume that what you write would be evaluated in practice mostly only one time. If you made your expressions fully type stable you have to pay each time (assuming that the expression changes):
the cost of compiling it (expensive)
the cost of running it (relatively cheap)
If your code would be type unstable you have to pay cost of compilation only once. It is true that running it will be a bit slower, but overall probably it will be better to do this this way.
On the other hand - if you expect that you define the expression only once and then run it many times, then probably a better approach is to use metaprogramming:
process your expression only once and generate Julia code that will be evaluating your expression
then Julia will compile the generated code once
you get a maximum performance of executing it after the steps 1 and 2 are made
A half-measure to your question would be to use the following data structure:
struct Binary{T <: BinaryKind, S} <: Operation
xs::Vector{S}
end
In this way your code will be type-stable if S is a concrete type or a small union of concrete types, and type unstable otherwise (and I expect that then you can try to make the rest of the code to generate xs in a way that its eltype is concrete or a small union of concrete types).
(if you have more questions on this please comment and I can expand the answer)
I have seen the following in other people's code, but I've never written it in my own since I didn't understand what was happening.
As an example:
function add(x::T, y::T) where {T :< Integer}
I am guessing that T is being cast as an Integer and then used to explicitly type x and y. But why not just do x::Int64? Does where {T :< Integer} allow for any Int type like Int32 and Int64?
To expand a little on Oscar's answer:
Using function add(x::T, y::T) where {T :< Integer} allows you to add a parametric method to the function add(x, y). You can read up on this in more detail in the Julia documentation under Parametric Methods.
This comes with two big advantages:
It allows you to define reasonably general methods (since in many cases the exact type of integer would not really affect the function definition). Simultaneously it allows you to restrict the call to x, y pairs of the same type, which can improve type stability and lead to more efficient compiled code.
function add(x::T, y::T) where {T :< Integer} means that the function can be called on any 2 variables who's type is the same subtype of Integer. This includes things like 2 bigInts, but not 1 Int64 and 1 Int32. This is called Diagonal Dispatch, and is a very useful pattern.
Edited for Clarity!
There are a couple of ways to build/generate an array in Julia.
I have been using the single quote or apostrophe approach for column vectors because it is quicker than multiple commas within the []'s:
julia> a = [1 2 3 4]'
4×1 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
1
2
3
4
This is generating what I believe to be a more complicated data type: "LinearAlgebra.Adjoint{Int64,Array{Int64,1}}"
In comparison to comma separated elements:
julia> a = [1,2,3,4]
4-element Array{Int64,1}:
1
2
3
4
Which generates an Array{Int64,1} type.
The Question(s):
Is the LinearAlgebra.Adjoint{...} type more computationally expensive then the base array? Should I avoid generating this array in a general sense?(i.e. outside modeling linear algebra)
It's possible there is a small difference that wouldn't matter on a smaller scope but, I plan to eventually preform operations on large data sets. Should I try to keep consistent with generating them as Array{Int64,1} types for these purposes?
Original
I've been learning Julia and I would like to develop good habits early; focusing on computational efficiency. I have been working with arrays and gotten comfortable with the single quote notation at the end to convert into a column vector. From what I'm understanding about the type system, this isn't just a quicker version than commas way to organize. Is using the comma computationally more expensive or semantically undesirable in general? It seems it wouldn't matter with smaller data sets but, what about larger data sets?(e.g. 10k computations)
Deleted original code example to avoid confusion.
Here's a performance example:
julia> a = rand(10^6);
julia> b = rand(1, 10^6)';
julia> typeof(a)
Array{Float64,1}
julia> typeof(b)
Adjoint{Float64,Array{Float64,2}}
julia> #btime sum($a)
270.137 μs (0 allocations: 0 bytes)
500428.44363296847
julia> #btime sum($b)
1.710 ms (0 allocations: 0 bytes)
500254.2267732659
As you can see, the performance of the sum over the Vector is much better than the sum over the Adjoint (I'm actually a bit surprised about how big the difference is).
But to me the bigger reason to use Vector is that it just seems weird and unnatural to use the complicated and convoluted Adjoint type. It is also a much bigger risk that some code will not accept an Adjoint, and then you've just made extra trouble for yourself.
But, really, why do you want to use the Adjoint? Is it just to avoid writing in commas? How long are these vectors you are typing in? If vector-typing is a very big nuisance to you, you could consider writing [1 2 3 4][:] which will return a Vector. It will also trigger an extra allocation and copy, and it looks strange, but if it's a very big deal to you, maybe it's worth it.
My advice: type the commas.
How do you define non-commutable variables? I'm new to julia and have been reading the doc and found nothing so far.
By non-commutable I mean if variables (say symbols) a and b are multiplied. ab =\= ba
About commutativity: Julia does not assume that a*b is the same as b*a (example: let a and b be matrices). However, methods of the function *(a,b) for some specific combinations of types act in a commutative manner, such as when a and b are numbers.
Since you are talking about symbols, I guess that want to work with a symbolic representation of expressions. There's at least two ways to go about this:
Work with the AST of an expression.
You can quote any expression by :(expr) (sometimes :expr is enough, depends on operator precedence):
julia> ex = :(x*y*z)
:(x * y * z)
julia> typeof(ex)
Expr
Note that the order of the factors has been preserved (though the associativity has not).
You can inspect the Expr by looking at it's head and args fields:
julia> ex.head
:call
julia> ex.args
4-element Array{Any,1}:
:*
:x
:y
:z
The args may be e.g. symbols (of type Symbol), constant values, and other expressions.
This technique works well if you want to write your own macros that process expressions, since the input to a macro is the AST of its arguments.
Define your own types and overload the * function. Example: with
abstract Symbolic
# might as well make these immutable
immutable Sym <: Symbolic
name::Symbol
end
immutable Prod <: Symbolic
x::Symbolic
y::Symbolic
end
# The * function is imported by default.
# Usually, we need to import a function
# from Base before extending it.
*(x::Symbolic, y::Symbolic) = Prod(x, y)
you can do
julia> x, y = Sym(:x), Sym(:y)
(Sym(:x),Sym(:y))
julia> x*y
Prod(Sym(:x),Sym(:y))
Since our *(x::Symbolic, y::Symbolic) function preserves the order of the arguments, we can see it in the created Prod object.
In most languages, the second alternative is your only option. The first alternative is more direct since you don't have to write a new AST framework for yourself, and don't have to execute the expression just to get at it's representation. Which one is more suitable depends on the application, however. If you want to associate different properties with your variables, the second approach seems easier.
Was it something like this that you were looking for?