How to port C++ union to Julia - julia

I am trying to port some code and now I've hit a sticky bit. The original code is in C++. I need to port a union that has two 32 bit ints (in an array) and a double.
So far I have:
I1 = UInt32(56) # arbitrary integer values for example
I2 = UInt32(1045195987)
# do transforms on I1 and I2 as per the code I'm porting
A = bits(I1)
B = bits(I2)
return parse(Float64, string(A,B))
Is this the way to do it? The string operation seems expensive. Any advice appreciated.

I also come from mostly C/C++ programming, and this is what I do to handle the problem:
First, create an immutable type with two UInt32 elements:
immutable MyType
a::UInt32
b::UInt32
end
Then you can convert a vector of Float64 to that type with reinterpret.
For example:
julia> x = [1.5, 2.3]
2-element Array{Float64,1}:
1.5
2.3
julia> immutable MyType ; a::UInt32 ; b::UInt32 ; end
julia> y = reinterpret(MyType, x)
2-element Array{MyType,1}:
MyType(0x00000000,0x3ff80000)
MyType(0x66666666,0x40026666)
julia> x[1]
1.5
julia> y[1]
MyType(0x00000000,0x3ff80000)
julia> y[1].a
0x00000000
julia> y[1].b
0x3ff80000
Note: the two vectors still point to the same memory, so you can even update elements, using either type.
julia> x[1] = 10e91
1.0e92
julia> y[1].a
0xbf284e24
julia> y[1].b
0x53088ba3
julia> y[1] = MyType(1,2)
MyType(0x00000001,0x00000002)
julia> x[1]
4.2439915824e-314

Related

Shorthand for Int32 literals in Julia

I use lots of Int32s in my code because I have some large arrays of those. But for some x::Int32 we have typeof(x+1) == Int64 since numeric literals are Int64 by default (I have to use 64bit Julia to handle my arrays). The problem is, if I have some function f(x::Int32) then f(x+1) will method error. I don't want to implement a f(x::Int64) = f(convert(Int32, x)) for almost every function and want to use concrete types for type stability. Currently, I simply have expressions like x + Int32(1) all over my code which looks really cluttered. For other types we have shorthands, i.e., 1.f0 gives me a Float32 and big"1" a BigInt. Is there something similar for Int32?
Since you explicitly mention the big_str macro (big"") you can easily define a similar macro for Int32 (the same way the uint128_str and int128_str is defined):
macro i32_str(s)
parse(Int32, s)
end
julia> typeof(i32"1")
Int32
this might still clutter your code too much so alternatively you could exploit that a number followed by a name is multiplication:
struct i32 end
(*)(n, ::Type{i32}) = Int32(n)
julia> typeof(1i32)
Int32
You can make a macro to replace every literal integer with an Int32, a bit like what ChangePrecision.jl does for floats. A very quick first attempt is:
julia> macro literal32(ex)
esc(literal32(ex))
end;
julia> literal32(ex::Expr) = Expr(ex.head, literal32.(ex.args)...);
julia> literal32(i::Int) = Int32(i);
julia> literal32(z) = z; # ignore Symbol, literal floats, etc.
julia> #literal32 [1,2] .+ 3
2-element Vector{Int32}:
4
5
julia> #literal32 function fun(x::AbstractVector)
x[1] + 2 # both 1 and 2 are changed
end
fun (generic function with 1 method)
julia> fun(Int32[3,4]) |> typeof
Int32
One place this may have unexpected consequences is literal type parameters:
julia> #literal32([1,2,3]) isa Array{Int32,1}
true
julia> #literal32 [1,2,3] isa Array{Int32,1}
false
Another is that x^2 will not use Base.literal_pow, e.g. #literal32 Meta.#lower pi^2.
What if you say:
# Or, a::Int32 = 1
julia> a = Int32(1)
1
julia> b::Int32 = a+2
3
julia> typeof(b)
Int32
julia> f(b)
...

how to perform reduce(hcat, <Vector{Vector}>) by TensorCast.jl?

Is it possible to do the following via TensorCast.jl as well?
#This is an object of type Vector{Vector{Float64}}
julia> a = [rand(5) for i=1:5];
julia> reduce(hcat, a)
5×5 Matrix{Float64}:
0.0678613 0.266194 0.183221 0.485462 0.873282
0.735101 0.925276 0.956102 0.333281 0.885147
0.323555 0.74204 0.135538 0.26123 0.261068
0.81847 0.917006 0.0118195 0.295497 0.712431
0.420139 0.0197552 0.0617039 0.157477 0.500931
I tried something super silly, I guess (😰):
julia> #reduce b := hcat(a)
ERROR: MethodError: no method matching guesstarget(::Nothing, ::Vector{Any}, ::Vector{Any})
Closest candidates are:
guesstarget(::Expr, ::Any, ::Any) at C:\Users\Shayan\.julia\packages\TensorCast\mQB8h\src\macro.jl:1330
Just do:
#cast b[j,i] := a[i][j]
Example:
julia> a = [rand(3) for _ in 1:2]
2-element Vector{Vector{Float64}}:
[0.20012490537057803, 0.0365551498875093, 0.02494737196890595]
[0.6563493855249903, 0.181706254856571, 0.29210798163726615]
julia> #cast b[j,i] := a[i][j]
3×2 lazystack(::Vector{Vector{Float64}}) with eltype Float64:
0.200125 0.656349
0.0365551 0.181706
0.0249474 0.292108
julia> (#cast b[j,i] := a[i][j]) == reduce(hcat, a)
true
Exaplanation
The variables i and j are used by #cast to define the matrix layout - we have a Vector of Vectors and hence on the right side you can see a[i][j], := defines a new Matrix and b[j,i] says where the corresponding i and j elements should go. Note that you do not declare i and j variables - this is handled by the macro.
Finally, note that if you want the result to be materialized to an actual matrix (rather than a lazystack) you can next do collect(b)

Double Broadcasting in Julia for Matrix Vector Addition?

Newbie Julia question here. Given two arrays,
W = [randn(3,2), randn(3,2)]
b = [randn(3), randn(3)]
I would like to do a "nested broadcast" along the lines of,
W .+ b = [W[1].+b[1], W[2].+b[2]]
So far the best I've been able to come up with is,
[Wi.+bi (Wi,bi) for zip(W,b)]
Coming from a Python background this feels sacrilegious. Is there a better way to do this in Julia?
You could do something like the following:
julia> W = [randn(3,2), randn(3,2)]
2-element Array{Array{Float64,2},1}:
[0.39179718902868116 -0.5387622679356612; -0.594274465053327 0.018804631512093436; -2.273706742420988 -0.4638617400026042]
[0.3249960563405678 -0.4877554417492699; 0.5036437919340767 1.3172770503034696; 0.03501532820428975 -0.2675024677340758]
julia> b = [randn(3), randn(3)]
2-element Array{Array{Float64,1},1}:
[1.2571527266220441, 0.21599608118129476, 0.21498843153804936]
[-0.528960345932853, 0.5610435189953311, -0.8636370930615718]
# A helper function which broadcasts once
julia> s_(Wi, bi) = Wi .+ bi
s_ (generic function with 1 method)
# Broadcast the helper function
julia> s_.(W, b)
2-element Array{Array{Float64,2},1}:
[1.6489499156507252 0.718390458686383; -0.3782783838720323 0.2348007126933882; -2.0587183108829388 -0.24887330846455485]
[-0.20396428959228524 -1.016715787682123; 1.0646873109294077 1.8783205692988008; -0.828621764857282 -1.1311395607956476]
As mentioned in comments, you can use one of the available user-definable infix operators to name the helper function, which allows for a nicer syntax (the particular symbol used below can be obtained by typing \oplus then Tab):
julia> ⊕(x, y) = x .+ y
⊕ (generic function with 1 method)
julia> W .⊕ b
2-element Array{Array{Float64,2},1}:
[1.6489499156507252 0.718390458686383; -0.3782783838720323 0.2348007126933882; -2.0587183108829388 -0.24887330846455485]
[-0.20396428959228524 -1.016715787682123; 1.0646873109294077 1.8783205692988008; -0.828621764857282 -1.1311395607956476]
Or - again as mentioned in comments - if you don't feel like explicitly declaring a helper function:
julia> ((Wi, bi)->Wi.+bi).(W, b)
2-element Array{Array{Float64,2},1}:
[1.6489499156507252 0.718390458686383; -0.3782783838720323 0.2348007126933882; -2.0587183108829388 -0.24887330846455485]
[-0.20396428959228524 -1.016715787682123; 1.0646873109294077 1.8783205692988008; -0.828621764857282 -1.1311395607956476]
(I personally have more difficulty reading that last version, but YMMV)
When you want to broadcast broadcast, then broadcast broadcast:
julia> broadcast.(+, W, b)
2-element Array{Array{Float64,2},1}:
[-0.7364111670769904 0.010994354421031916; -0.9010128415786036 0.22868802910609998; 1.2030371118617933 0.21305414210853912]
[0.19183885867446926 0.5362077496502086; 1.5909421118115665 0.1235808501390212; 1.5190965380769597 0.1883638848487652]
julia> [W[1].+b[1], W[2].+b[2]]
2-element Array{Array{Float64,2},1}:
[-0.7364111670769904 0.010994354421031916; -0.9010128415786036 0.22868802910609998; 1.2030371118617933 0.21305414210853912]
[0.19183885867446926 0.5362077496502086; 1.5909421118115665 0.1235808501390212; 1.5190965380769597 0.1883638848487652]

Assign vector components to different variables in Julia

I want to assign the result of an operation to a concatenation of variables in Julia. Something similar to this (although this doesn't work):
a = zeros(5)
b = zeros(5)
a, b .= rand(10)
Is it possible? Thank you.
You are looking for "vector view concatenation". The idea here is to use SubArrays to build an Array that is actually a view into two arrays. Julia does not support this out of the box. The Julia package ChainedVectors.jl was built for this, but it is heavily outdated and only works with Julia <= 0.4.
Not everything is lost. You have two alternatives:
Use CatViews.jl
As pointed out in the comments, CatViews.jl is like ChainedVectors.jl, but works with Julia 0.6 and 0.7:
Pkg.add("CatViews")
using CatViews
a = zeros(2)
b = zeros(2)
CatView(a, b) .= rand(4)
Build your own solution
With a little work, we can get as good as
a = zeros(2)
b = zeros(2)
MyView(a, b) .= rand(4)
Julia allows you to build your own view-concatenation type. The effort required to build it scales proportional to how general you want it to be. Here is a first attempt that works with vectors:
julia> # Create a type for a view into two vectors.
julia> type MyView{T} <: AbstractVector{T}
a::Vector{T}
b:: Vector{T}
end
julia> import Base: size, getindex, setindex!
julia> # Define methods to make MyView behave properly.
julia> size(c::MyView) = size(c.a) .+ size(c.b)
julia> getindex(c::MyView, i::Int) = i <= length(c.a) ? getindex(a, i) : getindex(b, i-length(a))
julia> setindex!(c::MyView, val, i::CartesianIndex) = i[1] <= length(c.a) ? setindex!(c.a, val, i[1]) : setindex!(c.b, val, i[1]-length(a))
julia> setindex!(c::MyView, val, i::Int) = i <= length(c.a) ? setindex!(c.a, val, i) : setindex!(c.b, val, i-length(a))
julia> # Test MyView. Define two arrays and put them
julia> # into a single view.
julia> a = rand(2)
2-element Array{Float64,1}:
0.701867
0.543514
julia> b = rand(2)
2-element Array{Float64,1}:
0.00355893
0.405809
julia> MyView(a, b) .= rand(4)
4-element MyView{Float64}:
0.922896
0.969057
0.586866
0.457117
julia> # Hooray, it worked! As we see below,
julia> # the individual arrays were updated.
julia> a
2-element Array{Float64,1}:
0.922896
0.969057
julia> b
2-element Array{Float64,1}:
0.586866
0.457117
This?
a .= x[1:5]
b .= x[6:end]
You must tell Julia somehow where to split the vector.

Re. partitions()

Why is
julia> collect(partitions(1,2))
0-element Array{Any,1}
returned instead of
2-element Array{Any,1}:
[0,1]
[1,0]
and do I really have to
x = collect(partitions(n,m));
y = Array(Int64,length(x),length(x[1]));
for i in 1:length(x)
for j in 1:length(x[1])
y[i,j] = x[i][j];
end
end
to convert the result to a two-dimensional array?
From the wikipedia:
In number theory and combinatorics, a partition of a positive integer n, also called an integer partition, is a way of writing n as a sum of positive integers.
For array conversion, try:
julia> x = collect(partitions(5,3))
2-element Array{Any,1}:
[3,1,1]
[2,2,1]
or
julia> x = partitions(5,3)
Base.FixedPartitions(5,3)
then
julia> hcat(x...)
3x2 Array{Int64,2}:
3 2
1 2
1 1
Here's another approach to your problem that I think is a little simpler, using the Combinatorics.jl library:
multisets(n, k) = map(A -> [sum(A .== i) for i in 1:n],
with_replacement_combinations(1:n, k))
This allocates a bunch of memory, but I think your current approach does too. Maybe it would be useful to make a first-class version and add it to Combinatorics.jl.
Examples:
julia> multisets(2, 1)
2-element Array{Array{Int64,1},1}:
[1,0]
[0,1]
julia> multisets(3, 5)
21-element Array{Array{Int64,1},1}:
[5,0,0]
[4,1,0]
[4,0,1]
[3,2,0]
[3,1,1]
[3,0,2]
[2,3,0]
[2,2,1]
[2,1,2]
[2,0,3]
⋮
[1,2,2]
[1,1,3]
[1,0,4]
[0,5,0]
[0,4,1]
[0,3,2]
[0,2,3]
[0,1,4]
[0,0,5]
The argument order is backwards from yours to match mathematical convention. If you prefer the other way, that can easily be changed.
one robust solution can be achieved using lexicographic premutations generation algorithm, originally By Donald Knuth plus classic partitions(n).
that is lexicographic premutations generator:
function lpremutations{T}(a::T)
b=Vector{T}()
sort!(a)
n=length(a)
while(true)
push!(b,copy(a))
j=n-1
while(a[j]>=a[j+1])
j-=1
j==0 && return(b)
end
l=n
while(a[j]>=a[l])
l-=1
end
tmp=a[l]
a[l]=a[j]
a[j]=tmp
k=j+1
l=n
while(k<l)
tmp=a[k]
a[k]=a[l]
a[l]=tmp
k+=1
l-=1
end
end
end
The above algorithm will generates all possible unique
combinations of an array elements with repetition:
julia> lpremutations([2,2,0])
3-element Array{Array{Int64,1},1}:
[0,2,2]
[2,0,2]
[2,2,0]
Then we will generate all integer arrays that sum to n using partitions(n) (forget the length of desired arrays m), and resize them to the lenght m using resize_!
function resize_!(x,m)
[x;zeros(Int,m-length(x))]
end
And main function looks like:
function lpartitions(n,m)
result=[]
for i in partitions(n)
append!(result,lpremutations(resize_!(i, m)))
end
result
end
Check it
julia> lpartitions(3,4)
20-element Array{Any,1}:
[0,0,0,3]
[0,0,3,0]
[0,3,0,0]
[3,0,0,0]
[0,0,1,2]
[0,0,2,1]
[0,1,0,2]
[0,1,2,0]
[0,2,0,1]
[0,2,1,0]
[1,0,0,2]
[1,0,2,0]
[1,2,0,0]
[2,0,0,1]
[2,0,1,0]
[2,1,0,0]
[0,1,1,1]
[1,0,1,1]
[1,1,0,1]
[1,1,1,0]
The MATLAB script from http://www.mathworks.com/matlabcentral/fileexchange/28340-nsumk actually behaves the way I need, and is what I though that partitions() would do from the description given. The Julia version is
# k - sum, n - number of non-negative integers
function nsumk(k,n)
m = binomial(k+n-1,n-1);
d1 = zeros(Int16,m,1);
d2 = collect(combinations(collect((1:(k+n-1))),n-1));
d2 = convert(Array{Int16,2},hcat(d2...)');
d3 = ones(Int16,m,1)*(k+n);
dividers = [d1 d2 d3];
return diff(dividers,2)-1;
end
julia> nsumk(3,2)
4x2 Array{Int16,2}:
0 3
1 2
2 1
3 0
using daycaster's lovely hcat(x...) tidbit :)
I still wish there would be a more compact way of doing this.
The the first mention of this approach seem to be https://au.mathworks.com/matlabcentral/newsreader/view_thread/52610, and as far as I can understand it is based on the "stars and bars" method https://en.wikipedia.org/wiki/Stars_and_bars_(combinatorics)

Resources