Creating an array with "hidden" extra indices - arrays

type ExtendedJumpArray{T,T2} <: AbstractArray{Float64,1}
u::T
jump_u::T2
end
Base.length(A::ExtendedJumpArray) = length(A.u)
Base.size(A::ExtendedJumpArray) = (length(A),)
function Base.getindex(A::ExtendedJumpArray,i::Int)
i <= length(A.u) ? A.u[i] : A.jump_u[i-length(A.u)]
end
function Base.setindex!(A::ExtendedJumpArray,v,i::Int)
i <= length(A.u) ? (A.u[i] = v) : (A.jump_u[i-length(A.u)] = v)
end
similar(A::ExtendedJumpArray) = deepcopy(A)
indices(A::ExtendedJumpArray) = Base.OneTo(length(A.u) + length(A.jump_u))
I thought I was the cool kid on the block, creating an array which could index past its length (I am doing it for a specific reason). But Julia apparently doesn't like this:
julia> ExtendedJumpArray([0.2],[-2.0])
Error showing value of type ExtendedJumpArray{Array{Float64,1},Array{Float64,1}}:
ERROR: MethodError: no method matching inds2string(::Int64)
Closest candidates are:
inds2string(::Tuple{Vararg{AbstractUnitRange,N}}) at show.jl:1485
in _summary(::ExtendedJumpArray{Array{Float64,1},Array{Float64,1}}, ::Int64) at .\show.jl:1490
in #showarray#330(::Bool, ::Function, ::IOContext{Base.Terminals.TTYTerminal}, ::ExtendedJumpArray{Array{Float64,1},Array{Float64,1}}, ::Bool) at .\show.jl:1599
in display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::MIME{Symbol("text/plain")}, ::ExtendedJumpArray{Array{Float64,1},Array{Float64,1}}) at .\REPL.jl:132
in display(::Base.REPL.REPLDisplay{Base.REPL.LineEditREPL}, ::ExtendedJumpArray{Array{Float64,1},Array{Float64,1}}) at .\REPL.jl:135
in display(::ExtendedJumpArray{Array{Float64,1},Array{Float64,1}}) at .\multimedia.jl:143
in print_response(::Base.Terminals.TTYTerminal, ::Any, ::Void, ::Bool, ::Bool, ::Void) at .\REPL.jl:154
in print_response(::Base.REPL.LineEditREPL, ::Any, ::Void, ::Bool, ::Bool) at .\REPL.jl:139
in (::Base.REPL.##22#23{Bool,Base.REPL.##33#42{Base.REPL.LineEditREPL,Base.REPL.REPLHistoryProvider},Base.REPL.LineEditREPL,Base.LineEdit.Prompt})(::Base.LineEdit.MIState, ::Base.AbstractIOBuffer{Array{UInt8,1}}, ::Bool) at .\REPL.jl:652
in run_interface(::Base.Terminals.TTYTerminal, ::Base.LineEdit.ModalInterface) at .\LineEdit.jl:1579
in run_frontend(::Base.REPL.LineEditREPL, ::Base.REPL.REPLBackendRef) at .\REPL.jl:903
in run_repl(::Base.REPL.LineEditREPL, ::Base.##932#933) at .\REPL.jl:188
in _start() at .\client.jl:360
Is there an easy way to do this without breaking the show methods, and whatever else may be broken? Or is there a better way to do this in general?

Indices needs to return a tuple, just like size.
julia> Base.similar(A::ExtendedJumpArray) = deepcopy(A)
julia> Base.indices(A::ExtendedJumpArray) = (Base.OneTo(length(A.u) + length(A.jump_u)),)
julia> ExtendedJumpArray([0.2],[-2.0])
2-element ExtendedJumpArray{Array{Float64,1},Array{Float64,1}}:
0.2
-2.0
julia> length(ans)
1
Having indices and size disagree in the dimensionality of an array, though, is likely to end with confusion and strife. Some functions use size, whereas others use indices. See display vs. length above.

Related

Julia: how to index an 'any' type array

I am new in Julia. I want to replicate the results in Wollmann (2019).
However, it always give me this error: LoadError: MethodError: no method matching setindex!(::Type{Array{Any, 100}}, ::Matrix{Int64}, ::Int64).
Here is the code:
i=1
hArray = Array{Any,100}
tbx_temp = (tbx_t.==(1996-1985))+(tbx_t.==(2000-1985))+(tbx_t.==(2005-1985))
hArray[i]=tbx_temp.*tbx_enter.*(sum(tbx_x1[:,3:4],dims=2).==0) .* sum(tbx_h_x[:,2:4],dims=2); i=i+1;
#tbx_t is an Int64 vector, and looks like this:
13824-element Vector{Int64}:
[1;1;1;...;27;27;27]
#tbx_enter is a bitvector, either 0 or 1.
#tbx_x1 is a float64 matrix:
13824×2 Matrix{Float64}:
[1.0 0.0; 0.0 1.0;...;0.0 0.0; 0.0 0.0]
#tbx_h_x is a bitmatrix, either 0 or 1
13824×3 BitMatrix:
[0 0 0; 0 1 0;...;1 0 1; 0 0 0]
Thanks in advance!
The line hArray = Array{Any,100} simply describes the type of a 100-dimensional array. It does not construct a 100-element vector. To create such an array (without initializing its elements), use the Array constructor, or perhaps more clearly, the Vector constructor:
# Using Array constructor
hArray = Array{Any}(undef, 100)
#Using Vector constructor
hArray = Vector{Any}(undef, 100)
However, it is typically poor practice to use the Any type without good reason. For example, performance can decrease when using abstract types. Your snippet indicates that the elements of hArray are likely Float64. If that is the case, you can use:
hArray = Vector{Float64}(undef, 100)
Even if you aren't sure exactly which data type is appropriate, using a smaller abstract type such as Number or Real is better practice than using Any.

Why does a subtype of AbstractArray result in imprecise matrix operations in Julia?

I'm currently working on creating a subtype of AbstractArray in Julia, which allows you to store a vector in addition to an Array itself. You can think of it as the column "names", with element types as a subtype of AbstractFloat. Hence, it has some similarities to the NamedArray.jl package, but restricts to only assigning the columns with Floats (in case of matrices).
The struct that I've created so far (following the guide to create a subtype of AbstractArray) is defined as follows:
struct FooArray{T, N, AT, VT} <: AbstractArray{T, N}
data::AT
vec::VT
function FooArray(data::AbstractArray{T1, N}, vec::AbstractVector{T2}) where {T1 <: AbstractFloat, T2 <: AbstractFloat, N}
length(vec) == size(data, 2) || error("Inconsistent dimensions")
new{T1, N, typeof(data), typeof(vec)}(data, vec)
end
end
#inline Base.#propagate_inbounds Base.getindex(fooarr::FooArray, i::Int) = getindex(fooarr.data, i)
#inline Base.#propagate_inbounds Base.getindex(fooarr::FooArray, I::Vararg{Int, 2}) = getindex(fooarr.data, I...)
#inline Base.#propagate_inbounds Base.size(fooarr::FooArray) = size(fooarr.data)
Base.IndexStyle(::Type{<:FooArray}) = IndexLinear()
This already seems to be enough to create objects of type fooArray and do some simple math with it. However, I've observed that some essential functions such as matrix-vector multiplications seem to be imprecise. For example, the following should consistently return a vector of 0.0, but:
R = rand(100, 3)
S = FooArray(R, collect(1.0:3.0))
y = rand(100)
S'y - R'y
3-element Vector{Float64}:
-7.105427357601002e-15
0.0
3.552713678800501e-15
While the differences are very small, they can quickly add up over many different calculations, leading to significant errors.
Where do these differences come from?
A look at the calculations via macro #code_llvm reveals that appearently different matmul functions from LinearAlgebra are used (with other minor differences):
#code_llvm S'y
...
# C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\LinearAlgebra\src\matmul.jl:111 within `*'
...
#code_llvm S'y
...
# C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\LinearAlgebra\src\matmul.jl:106 within `*'
...
Redefining the adjoint and * functions on our FooArray object provides the expected, correct result:
import Base: *, adjoint, /
Base.adjoint(a::FooArray) = FooArray(a.data', zeros(size(a.data, 1)))
*(a::FooArray{T, 2, AT, VT} where {AT, VT}, b::AbstractVector{S}) where {T, S} = a.data * b
S'y - R'y
3-element Vector{Float64}:
0.0
0.0
0.0
However, this solution (which is also done in NamedArrays here) would require defining and maintaining all sorts of functions, not just the standard functions in base, adding more and more dependencies just because of this small error margin.
Is there any simpler way to get rid of this issue without redefining every operation and possibly many other functions from other packages?
I'm using Julia version 1.6.1 on Windows 64-bit system.
Yes, the implementation of matrix multiplication will vary depending upon your array type. The builtin Array will use BLAS, whereas your custom fooArray will use a generic implementation, and due to the non-associativity of floating point arithmetic, these different approaches will indeed yield different values — and note that they may be different from the ground truth, even for the builtin Arrays!
julia> using Random; Random.seed!(0); R = rand(100, 3); y = rand(100);
julia> R'y - Float64.(big.(R)'big.(y))
3-element Vector{Float64}:
-3.552713678800501e-15
0.0
0.0
You may be able to implement your custom array as a DenseArray, which will ensure that it uses the same (BLAS-enabled) codepath. You just need to implement a few more methods, most importantly strides and unsafe_convert:
julia> struct FooArray{T, N} <: DenseArray{T, N}
data::Array{T, N}
end
Base.getindex(fooarr::FooArray, i::Int) = fooarr.data[i]
Base.size(fooarr::FooArray) = size(fooarr.data)
Base.IndexStyle(::Type{<:FooArray}) = IndexLinear()
Base.strides(fooarr::FooArray) = strides(fooarr.data)
Base.unsafe_convert(P::Type{Ptr{T}}, fooarr::FooArray{T}) where {T} = Base.unsafe_convert(P, fooarr.data)
julia> R = rand(100, 3); S = FooArray(R); y = rand(100)
R'y - S'y
3-element Vector{Float64}:
0.0
0.0
0.0
julia> R = rand(100, 1000); S = FooArray(R); y = rand(100)
R'y == S'y
true

Is there a way to turn a array into a integer or float?

I'm trying to change an array with int into a single int in Julia 1.5.4 like that:
x = [1,2,3]
Here i would try or use a code/command (here: example())
x_new = example(x)
println(x_new)
typeof(x_new)
Ideal output would be something like this :
123
Int32
I already tried to solve this problem with parse() or push!() or something like this. But nothing worked well.
I couldn't find a similar problem...
You can find an issue about adding this functionality to Julia here: https://github.com/JuliaLang/julia/issues/40393
Bottom line, you don't want to use strings, and you should avoid unnecessary exponentiation, both of which will be really slow.
A very brief solution is
evalpoly(10, reverse([1,2,3]))
Spelling it out a bit more, you can do this
function joindigits(xs)
val = 0
for x in xs
val = 10*val + x
end
return val
end
Is this what you need?
julia> x = [1,2,3]
3-element Vector{Int64}:
1
2
3
julia> list2int(x) = sum(10 .^ (length(x)-1:-1:0) .* x)
list2int (generic function with 1 method)
julia> list2int(x)
123
You are looking for string concatenation and then parsing:
x_new = parse(Int64, string(x...))
Another interesting way to convert many small numbers to a bigger one is to combine raw bytes:
julia> reinterpret(Int16, [Int8(2),Int8(3)])
1-element reinterpret(Int16, ::Vector{Int8}):
770
Note that 770 = 256*3 + 2
Or for actual Ints:
julia> reinterpret(Int128, [10,1])
1-element reinterpret(Int128, ::Vector{Int64}):
18446744073709551626
(note that result is exactly Int128(2)^64+10)

Two different types of arrays of arrays

I'm confused about the different types of arrays of arrays. Consider these two examples
a = Array{Float64}[]
push!(a,[1, 2])
push!(a,[3, 4])
push!(a,[1 2; 3 4])
b = Array[[1.0, 2.0], [3.0,4.0], [1.0 2.0; 3.0 4.0]]
I'm not sure how a and b differ. Suppose I intend to run a for loop over each of the elements in a or b, and multiple each element by 2. That is,
for i in 1:3 a[i] = a[i]*2 end
for i in 1:3 b[i] = b[i]*2 end
I time the run time of both lines respectively, but they are equally fast. Are a and b the same? If so, why does a even exist? It looks fairly complicated, because typeof(a) yields "Array{Array{Float64,N} where N,1}". What does where do here?
Both a and b are vectors but they allow different type of elements. You can check this by writing:
julia> typeof(a)
Array{Array{Float64,N} where N,1}
julia> typeof(b)
Array{Array,1}
Now Array isa parametric type taking two parameters. The first parameter is type of elements that it allows. The second parameter is the dimension. You can see that in both cases the dimension is 1 which means both a and b are vectors. You can also check it using the ndims function:
julia> ndims(a)
1
julia> ndims(b)
1
The first parameter is allowed element type. In the case of a it is Array{Float64,N} where N while in the case of b just Array is printed. Before I explain how to read them note that the first parameter can be extracted using the eltype function:
julia> eltype(a)
Array{Float64,N} where N
julia> eltype(b)
Array
You can see that both a and b allow Array to be stored. First let me explain how to read Array{Float64, N} where N. It means that a allows storing of arrays of Float64 of any dimension. Actually you could have written in a shorter way like this Array{Float64} as you can check that:
julia> (Array{Float64,N} where N) === Array{Float64}
true
The reason is that if you do not put a restriction on a tail parameter it can be dropped in syntax. The where N part is a restriction on the parameter. In this case there is no restriction on the second parameter.
Now we can turn to b. You see its eltype is just Array, so both parameters are dropped, thus there are no restrictions on them as explained above. So Array is the same as Array{T, N} where {T,N} as you can see here:
julia> (Array{T, N} where {T,N}) === Array
true
So the difference is that a can store arrays of any dimension but they must have Float64 element type, while b can store arrays of any dimension and any element type. The distinction, in this case, has no performance impact as you have noted, but will have an impact on what can be stored in a and b. Here are some examples.
In this case they work the same, as you try to store an Int in them, but they allow only arrays:
julia> a[1] = 1
ERROR: MethodError: Cannot `convert` an object of type
julia> b[1] = 1
ERROR: MethodError: Cannot `convert` an object of type
but here they differ:
julia> a[1] = ["a"]
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Float64
julia> b[1] = ["a"]
1-element Array{String,1}:
"a"
julia> a
3-element Array{Array{Float64,N} where N,1}:
[1.0, 2.0]
[3.0, 4.0]
[1.0 2.0; 3.0 4.0]
julia> b
3-element Array{Array,1}:
["a"]
[3.0, 4.0]
[1.0 2.0; 3.0 4.0]
As you can see you can store an array of String in b, but this is not allowed to be stored in a.
Two additional comments (both topics are a bit complex so I leave out the details, but just give you hints what is going on):
You are allowed not to specify element type of an array when defining it. In this case Julia will automatically pick its element type:
julia> [[1.0, 2.0], [3.0,4.0], [1.0 2.0; 3.0 4.0]]
3-element Array{Array{Float64,N} where N,1}:
[1.0, 2.0]
[3.0, 4.0]
[1.0 2.0; 3.0 4.0]
The performance impact of the choice of element type of an array depends on the fact:
if the type is abstract (that can be checked by isabstracttype; this impact type inference made by the compiler),
if it is bits type (that can be checked by isbitstype; this impacts the storage layout),
if the element type is Union (small unions can are handled more efficiently).
In your case both element types are abstract and non-bits so the performance will be the same.

MethodError: no method matching -(::Int64, ::Array{Int64,1})

I tried playing around with this example in the Julia documentation. My attempt was to make the cell split into two parts that have half of the amount of protein each.
using OrdinaryDiffEq
const α = 0.3
function f(du,u,p,t)
for i in 1:length(u)
du[i] = α*u[i]/length(u)
end
end
function condition(u,t,integrator) # Event when event_f(u,t) == 0
1-maximum(u)
end
function affect!(integrator)
u = integrator.u
idxs = findall(x->x>=1-eps(eltype(u)),u)
resize!(integrator,length(u)+length(idxs))
u[idxs] ./ 2
u[end-idxs:end] = 0.5
nothing
end
callback = ContinuousCallback(condition,affect!)
u0 = [0.2]
tspan = (0.0,10.0)
prob = ODEProblem(f,u0,tspan)
sol = solve(prob,Tsit5(),callback=callback)
I get the error: MethodError: no method matching -(::Int64, ::Array{Int64,1}). I know there is a problem with idxs = findall(x->x>=1-eps(eltype(u)),u), and my attempt was to put a dot between the 1 and eps, but that didn't fix it. I am using Julia 1.1.1.
Running your code the stacktrace points to the line
u[end-idxs:end] = 0.5
The issue here is that findall returns an array even when it only finds one element, e.g.
julia> findall(x -> x > 2, [1,2,3])
1-element Array{Int64,1}:
3
and you can't subtract an array from end in your indexing expression.
I don't understand enough about your code to figure out what idxs should be, but if you expect this to only return one element you could either use first(idxs) (or even only(idxs) in Julia 1.4), or replace findall with findfirst, which returns the index as an Integer (rather than an array).

Resources