In Julia, I can easily create a graph using the very nicely written Graphs.jl library:
julia> using Graphs
julia> g = SimpleDiGraph(2)
{2, 0} directed simple Int64 graph
julia> add_edge!(g, 1,2)
true
julia> add_edge!(g, 2, 1)
true
However, I cannot seem to make the nodes anything other than integers. To wit, I would like to do something like this:
julia> using Graphs
julia> abstract type Foo end
julia> s = SimpleDiGraph(typeof(Foo)) # doesn't work.
julia> mutable struct Bar <: Foo
b::Bool
end
julia> mutable struct Baz <: Foo
b::Bool
end
julia> bar = Bar(true)
Bar(true)
julia> baz = Baz(false)
Baz(false)
julia> add_edge!(g, bar, baz) # Again, doesn't work.
How can I make the nodes a custom type?
Note: I have tried the following workaround, as it appears that only integral types are allowed in the nodes:
julia> add_edge!(g, Int64(pointer_from_objref(bar)), Int64(pointer_from_objref(baz)))
but this one, although not throwing an exception, returns false.
In the Graphs.jl tutorial, it is stated that "integers, and only integers, may be used for describing vertices." So my first goal is out of the question (although it may be possible in, say, MetaGraphs.jl). The recommendation is to use the integer value in the vertex as an index into a vector and store the objects in the vector.
Moreover, as it is stated that the goal is to index into a vector, the integers must be contiguous, and hence my second idea of using the address of the object is also not feasible.
However, using the graph to index into a vector is a perfectly acceptable solution for me.
Related
Is there in Julia a Collection type from which both Set and Array derive ?
I have both:
julia> supertype(Array)
DenseArray{T,N} where N where T
julia> supertype(DenseArray)
AbstractArray{T,N} where N where T
julia> supertype(AbstractArray)
Any
And:
julia> supertype(Set)
AbstractSet{T} where T
julia> supertype(AbstractSet)
Any
What I try to achieve is to write function that can take both Array or Set as argument, because the type of collection doesn't matter as long as I can iterate over it.
function(Collection{SomeOtherType} myCollection)
for elem in myCollection
doSomeStuff(elem)
end
end
No, there is no Collection type, nor is there an Iterable one.
In theory, what you ask can be accomplished through traits, which you can read about elsewhere. However, I would argue that you should not use traits here, and instead simply refrain from restricting the type of your argument to the function. That is, instead of doing
foo(x::Container) = bar(x)
, do
foo(x) = bar(x)
There will be no performance difference.
If you want to restrict your argument types you could create a type union:
julia> ty = Union{AbstractArray,AbstractSet}
Union{AbstractSet, AbstractArray}
julia> f(aarg :: ty) = 5
f (generic function with 1 method)
This will work on both sets and arrays
julia> f(1:10)
5
julia> f(rand(10))
5
julia> f(Set([1,2,5]))
5
But not on numbers, for example
julia> f(5)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
f(::Union{AbstractSet, AbstractArray}) at REPL[2]:1
Is it possible to have something like a list comprehension for building complicated expressions in Julia?
For example, say I have some symbols and types, and want to build a type from them. Right now, I have to do something like.
syms = [:a, :b, :c]
typs = [Int, Float32, Char]
new_type = :(type Foo end)
new_type.args[3].args = [:($sym::$typ) for (sym,typ) in zip(syms,typs)]
This works in that new_type is an expression containing
:(type Foo
a::Int64
b::Float32
c::Char
end)
But building complicated expressions like this is both extremely error prone (because you have be intimately knowledgeable with the Expr data type in order to know, eg that the expressions for the data types of the tuple have to be stored in new_type.args[3].args) and also extremely brittle in that any change to the AST of the expression being built would mean having to change where/how every sub-expression is stored.
So is there a way to do something like
:(type Foo
$(sym::typ for (sym,typ) in zip(syms,typs))
end)
and end up with the same expression as above?
Yes, you can splat arrays of expressions directly into the syntax:
julia> :(type Foo
$([:($sym::$typ) for (sym,typ) in zip(syms,typs)]...)
end)
:(type Foo # none, line 2:
a::Int64
b::Float32
c::Char
end)
Is it possible to create a function which takes a ::Vector{DataType} but constrains all members to be types which inherit from a particular abstract type?
Example:
# this code works but does not constrain the type
function foo{D<:DataType}(arr::Vector{D})
...
end
# this is kind of the syntax I'd like, but Type{Int} !<: Type{Integer}
function bar{D<:Type{Integer}}(arr::Vector{D})
...
end
Thank you
I'm not sure this is possible (cleanly) with a compile-time check. You could consider using a Val type, but this will be messy and probably slower. I would just make it a run-time check:
julia> function bar{T}(::Type{T}, arr::Vector{DataType})
if all(x->x<:T, arr)
println("ok")
else
println("bad")
end
end
bar (generic function with 1 method)
julia> bar(Integer, [Int,Int32])
ok
julia> bar(Integer, [Int,Int32,Float64])
bad
What's your use case for this? There might be an alternative that's cleaner.
just to clarify why function bar{T<:Integer}(arr::Vector{Type{T}}) = println(arr) won't work.
in a nutshell, this is because julia's type parameter is invariant.
firstly, take a look a OP's definition:
function bar{D<:Type{Integer}}(arr::Vector{D})
...
end
the problem here, as OP pointed out, is Type{Int} !<: Type{Integer}.
the reason is that Type{T} is a parametric type, even though Int <: Integer, we don't have Type{Int} <: Type{Integer}.
"bearing in mind"(yes, that's sarcasm) that the type parameter of julia's parametric type is invariant, i suggested to use this version:
function bar{T<:Integer}(arr::Vector{Type{T}})
...
end
it seems good! this time i'm using T instead of Type{T}, so i won't fall into the pit of Type{Int} !<: Type{Integer}.
however, as i wrote down that comment, i had just fallen into another pit -- Vector{} is also a parametric type. even if DataType <: Type{T}, we don't have Vector{DataType} <: Vector{Type{T}}.
as a result, a error will occur when running bar([Int64, Int32]).
julia> bar([Int64, Int32])
ERROR: MethodError: `bar` has no method matching bar(::Array{DataType,1})
julia> methods(bar)
bar{T<:Integer}(arr::Array{Type{T<:Integer},1})
julia> [Int64, Int32]
2-element Array{DataType,1}:
Int64
Int32
EDIT:
hmm, it seems that this problem is not that simple. the key point here is the mysterious relationship between DataType and Type{T}.
# we know that DataType is a subtype of Type{T},
# where T is a `TypeVar` \in [Bottom, Any].
julia> subtypes(Type)
3-element Array{Any,1}:
DataType
TypeConstructor
Union
julia> S = TypeVar(:S, Union{}, Integer, true)
S<:Integer
# but Type{S} is not a subtype of DataType
julia> Type{S} <: DataType
false
julia> Type{S} <: Type
true
i therefore conclude that it's impossible to make ::Vector{DataType} work in your case.
DataType has NO type parameters.
the below definition won't work, which seems like a bug.
julia> a = Array(Type{S}, 2)
2-element Array{Type{S<:Integer},1}:
#undef
#undef
julia> a[1] = Type{Int32} # or Int32
Type{Int32}
julia> a[2] = Type{Float32} # or Float32
Type{Float32}
julia> a
2-element Array{Type{S<:Integer},1}:
Type{Int32}
Type{Float32}
i'll post a question about this strange behavior. #Mageek
According to http://julia.readthedocs.org/en/latest/manual/integers-and-floating-point-numbers/, one should be able to do this:
julia> Float32(-1.5)
-1.5f0
Instead, I get:
julia> Float32(-1.5)
ERROR: type cannot be constructed
This happens for all other attempts to use this syntax, e.g. x = Int16(1)
I'm on 0.3.10.
You are on 0.3.10 but reading the manual for 0.5. In the manual for 0.3 http://julia.readthedocs.org/en/release-0.3/manual/integers-and-floating-point-numbers/
Values can be converted to Float32 easily:
julia> float32(-1.5)
-1.5f0
julia> typeof(ans)
Float32
In MatLab/Octave you could send a command "format long g" and have default numerical output in the REPL formatted like the following:
octave> 95000/0.05
ans = 1900000
Is it possible to get a similar behavior in Julia? Currently with julia
Version 0.3.0-prerelease+3930 (2014-06-28 17:54 UTC)
Commit bdbab62* (6 days old master)
x86_64-redhat-linux
I get the following number format.
julia> 95000/0.05
1.9e6
You can use the #printf macro to format. It behaves like the C printf, but unlike printf for C the type need not agree but is rather converted as necessary. For example
julia> using Printf
julia> #printf("Integer Format: %d",95000/0.05);
Integer Format: 1900000
julia> #printf("As a String: %s",95000/0.05);
As a String: 1.9e6
julia> #printf("As a float with column sized larger than needed:%11.2f",95000/0.05);
As a float with column sized larger than needed: 1900000.00
It is possible to use #printf as the default mechanism in the REPL because the REPL is implemented in Julia in Base.REPL, and in particular the following function:
function display(d::REPLDisplay, ::MIME"text/plain", x)
io = outstream(d.repl)
write(io, answer_color(d.repl))
writemime(io, MIME("text/plain"), x)
println(io)
end
To modify the way Float64 is displayed, you merely need to redefine writemime for Float64.
julia> 95000/0.05
1.9e6
julia> Base.Multimedia.writemime(stream,::MIME"text/plain",x::Float64)=#printf("%1.2f",x)
writemime (generic function with 13 methods)
julia> 95000/0.05
1900000.00