-
Notifications
You must be signed in to change notification settings - Fork 152
Creating a new array with a different element type? #217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The problem with I don't really understand your example code snippet actually: taking the first branch results in a type, but the second branch returns an instance. Now, that's not to say we've got all this stuff sorted out - there's certainly several API rough edges where we could do better. Perhaps you could describe your use case in a bit more detail? |
By the way, we do have julia> similar(SVector(1,2,3))
3-element MVector{3,Int64}:
140462817039632
140462499519488
0 Which is still a |
I'm not doing anything fancy here. I just want to throw an initialized value into a mutable type to update in the future. For a regular AbstractArray, that's an initialized vector that is mutated. For a number, that's a value that then gets updated each iteration. A StaticArray acts like a number but for some reason you can't just Yes, that example doesn't seem to work. I instead just have create the vector with changed units when it's a StaticArray by doing the mathematical operation (
That changes the type. |
The code that works for this part is if typeof(u) <: AbstractArray && !(typeof(u)<:SArray)
rate_prototype = similar(u/zero(t))
else
rate_prototype = u/zero(t)
end StaticArrays seem to require a large amount of special handling in ways that other types don't, so I am wondering if I am missing something. |
To the extent that To get away from mutating individual elements you can write algorithms at a higher level so that you operate on the whole set of vector components at once. In this way you can work with As a general comment, we have found that a lot of the implementation of array functionality in It would be easier to understand what you're trying to do if your example was a bit more complete (ie, I could actully run it). Here's an example of initializing a vector containing unitful quantities, and stripping them off: julia> using Unitful
julia> velocity = SVector(10.0u"m/s", 20.0u"m/s")
2-element SVector{2,Quantity{Float64, Dimensions:{𝐋 𝐓^-1}, Units:{m s^-1}}}:
10.0 m s^-1
20.0 m s^-1
julia> ustrip.(velocity)
2-element SVector{2,Float64}:
10.0
20.0 |
Everything for StaticArrays follows the path for
It's just building an using Unitful
# Number
A = 10.0u"m/s"
t = 1.0u"s"
B = A./t
# Every AbstractArray Except SArray?
A = [10.0u"m/s", 20.0u"m/s"]
t = 1.0u"s"
similar(A,recursive_eltype(A)/typeof(t)) # Recurse `eltype` for array of arrays, in RecursiveArrayTools
# Non-recursive arrays can just use `eltype`
# SArray
velocity = SVector(10.0u"m/s", 20.0u"m/s")
t = 1.0u"s"
B = A./t |
The key piece of information missing from your example is what you're going to do with Generally we try to make julia> using Unitful
julia> A = [10.0u"m/s", 20.0u"m/s"]
2-element Array{Quantity{Float64, Dimensions:{𝐋 𝐓^-1}, Units:{m s^-1}},1}:
10.0 m s^-1
20.0 m s^-1
julia> t = 1.0u"s"
1.0 s
julia> B = A ./ t
2-element Array{Quantity{Float64, Dimensions:{𝐋 𝐓^-2}, Units:{m s^-2}},1}:
10.0 m s^-2
20.0 m s^-2
julia> AS = @SVector [10.0u"m/s", 20.0u"m/s"]
2-element SVector{2,Quantity{Float64, Dimensions:{𝐋 𝐓^-1}, Units:{m s^-1}}}:
10.0 m s^-1
20.0 m s^-1
julia> BS = AS ./ t
2-element SVector{2,Quantity{Float64, Dimensions:{𝐋 𝐓^-2}, Units:{m s^-2}}}:
10.0 m s^-2
20.0 m s^-2 Both the syntax and semantics here are exactly the same. |
I was about to jump in and say exactly what @c42f just wrote.
In general,
If this were inside a longer function, it would be a no-op (well, ideally - compilers are strange and hard to predict). |
A = rand(10000)
using BenchmarkTools
@benchmark similar($A)
function g(A)
A ./ 2.0
end
@benchmark g($A)
I guess if it's just an initialization of arrays phase it's okay to take a performance hit like this, but I am still surprised that there's no easy and generic way to just build an array of similar shape with a different element type and will have to resort to actually doing to numerical calculation.
You mean,
No. The basic Why does
But that's essentially where the code differences come in. AbstractArray paths tend pre-allocate to use mutability: A .= B.*C Paths on numbers don't: A = B.*C # Normally don't `.`, but it's not harmful to do so (that's a simple example which is fixed by the In DiffEq, the whole stack "just works" on I find it really odd that if you put ApproxFun.jl |
I feel there is a little bit of confusion going on here - e.g. I don't understand what your example was trying to demonstrate. OTOH, you are quite justified in saying that it's hard to write generic code which works optimally for mutable and immutable objects, because it is!
Of course - you are completely correct. However, you would be reducing performance considerably if you just did function f(a::AbstractArray)
b = similar(a)
for i = 1:length(a)
@inbounds b[i] = 2*a[i]
end
return b
end compared to
Well, it's essential - this is really important. See the docstring, for example:
If you return a uninitialized array which is similar to the input, but immutable, then what could you possibly do with it? All you get is uninitialized elements which you can never update. Perhaps it gives you a snapshot of RAM or a bunch of zeros, but in general that array would be completely useless. One way forward might be to provide some way of initializing the output, so
I get what you are saying, but in the
Yes - I agree that this is a problem. Julia does not currently provide any good way of doing non-allocating generic programming over both mutables and immutables. I've opened a couple of discussions on this before, but not much progress has been made because (I think) it is a hard design problem. My observation is that the core devs were hoping to one day get to (a) elide reallocation of arrays automatically whenever it is safe to do so, so that you can use the same generic (and "functional") code paths for numbers, arrays, etc, and (b) allow an efficient and semantically correct way to "mutate an immutable", e.g. via a Philosophically, one could think of using mutability to avoid extra allocation as just an optimization. In an ideal world, I would suggest writing code in a reasonably functional form, and specialize it for mutable types. Of course, the mutable arrays in Julia are much more numerous and useful, so generally this will be done in reverse. Yes, it is frustrating to add another code path just for immutable arrays, and that So my advice is if you want to get optimal speed in these packages, it seems to me you have little choice but to "suck it up" and write a third code-path for immutable arrays. I am sorry that it's like this; we are well aware of the difficulty, and a variety of people are looking at how to make this easier in the future. |
Yeah, I guess that is the answer for now: assume |
Cool - let us know if you run into any more trouble. |
By the way, these issues and more are covered back in in #32 I agree it's painful to write efficient generic code with immutable vs mutable arrays right now. Perhaps it would help if we provided a trait from this in |
I was thinking of making a micro-package that defines the trait for all types in |
The only issue with this stuff is that it's only useful if it catches on. If you do this, DiffEq will make use of it, but hopefully packages which make types do. I guess it's useful even if just StaticArrays uses the traits. This is definitely something that would need to be in Base down the line though. |
I think FWIW, |
Yes, |
Usually
similar
returns the same type, but forSArray
it does not. Is there no solution for getting "the same array" with a new element type?That's the only solution I could come up with, and it requires separate codepaths every time an
SArray
is encounters and makes it tough to write generic codes. I am hoping there's a method I missed.The text was updated successfully, but these errors were encountered: