Skip to content
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

make assignment work on ustrip'ed StridedArrays #645

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

bjarthur
Copy link

fixes #644

note that i'm only superficially familiar with Unitful.jl, so am not sure if this breaks anything or is consistent with the roadmap. but the tests pass locally.

@codecov-commenter
Copy link

codecov-commenter commented Apr 15, 2023

Codecov Report

Merging #645 (c32177e) into master (4ce89e1) will not change coverage.
The diff coverage is 100.00%.

❗ Current head c32177e differs from pull request most recent head 6609979. Consider uploading reports for the commit 6609979 to get more accurate results

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

@@           Coverage Diff           @@
##           master     #645   +/-   ##
=======================================
  Coverage   89.07%   89.07%           
=======================================
  Files          16       16           
  Lines        1492     1492           
=======================================
  Hits         1329     1329           
  Misses        163      163           
Impacted Files Coverage Δ
src/utils.jl 95.34% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@sostock
Copy link
Collaborator

sostock commented Apr 17, 2023

This will also apply to ranges, which makes it different from the current behavior:

Before:

julia> r = (1:5)u"m"
(1:5) m

julia> ustrip(r)
1:1:5

After:

julia> r = (1:5)u"m"
(1:5) m

julia> ustrip(r)
5-element reinterpret(Int64, ::StepRange{Quantity{Int64, 𝐋, Unitful.FreeUnits{(m,), 𝐋, nothing}}, Quantity{Int64, 𝐋, Unitful.FreeUnits{(m,), 𝐋, nothing}}}):
 1
 2
 3
 4
 5

We could add a method ustrip(r::AbstractRange) = ustrip.(r) to keep the old behavior for ranges, but there are probably more types of AbstractArrays for which reinterpreting isn’t useful, like SArray from the StaticArrays package. Or does it not make a difference in that case (performance-wise)?

Reinterpreting is useful in two ways: to enable mutation of the original array, and to avoid allocations. For ranges and SArrays, both of these do not apply, because they are immutable and stack-allocated (unless you use mutable number types, which you could technically do).

We could implement ustrip(::typeof(reinterpret), ::AbstractArray{<:Quantity}) (see #630 (comment)) instead, that way we don’t add to the confusion of when reinterpreting happens and when it doesn’t.

@bjarthur
Copy link
Author

how about redefining ustrip(::Array... to dispatch on DenseArray instead of, as this PR currently stands, AbstractArray? doesn't make sense to reinterpret anything other than a dense array anyway, and doing so would preclude StaticArrays and Ranges, the two types you bring up, yet still cover GPUArrays, which is what motivates me.

julia> typeof(SVector(1,2,3)) <: DenseArray
false

julia> typeof(CuVector{Float64}([1,2,3])) <: DenseArray
true

julia> typeof(1:5) <: DenseArray
false

@sostock
Copy link
Collaborator

sostock commented Apr 18, 2023

how about redefining ustrip(::Array... to dispatch on DenseArray instead

I like that. Maybe we could even use StridedArray instead of DenseArray, which is more general and still makes sense. BLAS functions usually support StridedArrays.

@bjarthur bjarthur changed the title make assignment work on ustrip'ed AbstractArrays make assignment work on ustrip'ed StridedArrays Apr 18, 2023
@bjarthur
Copy link
Author

i tried StridedArrays and it works for my use case. have rebased the change into this PR. tests pass locally.

@bjarthur
Copy link
Author

anything else i need to do here? the CI is "awaiting approval".

@sostock
Copy link
Collaborator

sostock commented Apr 24, 2023

I just noticed that this is technically a breaking change. At least I consider it as one. A user that calls ustrip(view(…)) in their code may now unintentionally modify the original array, because ustrip used to create a new array before this PR.

This could be avoided if we implement this as ustrip(reinterpret, x). But if we do that, I would want it to error in cases where a wrong result would be returned (#630), which will require some more code.

@bjarthur
Copy link
Author

bjarthur commented Apr 24, 2023

ustrip used to create a new array before this PR because of a deprecated method.

perhaps it's time to bump the version to 2.0? what other new features are being held up because it would be a breaking change that we could implement if we did so?

certainly i'd like to see that deprecated method removed in v2.0. that should've been done in v0.5.

@sostock
Copy link
Collaborator

sostock commented Apr 25, 2023

I would like to release v2.0, but there are more breaking changes that I think should be included (and some of them require some further discussion), so it will take some time.

I still think we should use ustrip(reinterpret, x) for this functionality. It seems error-prone to me that ustrip(x) would return an object that shares its memory with x. Maybe I am biased because I did not know that this functionality existed until you opened #627, but I think many users don’t know of this feature, so it has the potential to lead to many errors. I think avoiding errors is more important than convenience.

Functions like view or reshape exist precisely for the purpose of reusing memory and allowing mutation of the original array. But ustrip is mainly a function that is applied to single values, not arrays. Therefore, I think it is not obvious that ustrip(x) might reuse memory.

Is there a reason why you think we shouldn’t add ustrip(reinterpret, x)? If you just think it is too long, maybe we could call it ustrip!(x) instead?

@bjarthur
Copy link
Author

should probably add the v2.0 label to this PR or the issue it fixes. i see there are two others with that label.

i don't find it confusing that ustrip on an array reinterprets. that is clearly documented and has existed for a long time. moreover, it is consistent with how arrays are passed in by pointer to functions. and it is probably what one would want to do normally anyway.

what is confusing is that ustriping a unit-less array or a strided one with units allocates.

that we disagree is an indication that more opinions should be solicited. who could we ping that is a heavy user of Unitful.jl?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

cannot assign to ustriped AbstractArrays
3 participants