Skip to content

Conversation

albertomercurio
Copy link
Contributor

The previous implementation of normalize was computing the division between the first element of the array and the norm, in order to obtain the output type. This is both inefficient and doesn't work on arrays that don't support scalar indexing.

I have changed the type check with float(Base.promote_eltype(a, nrm)).

Copy link

codecov bot commented Sep 23, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.90%. Comparing base (57ac0eb) to head (90d8447).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1458   +/-   ##
=======================================
  Coverage   93.90%   93.90%           
=======================================
  Files          34       34           
  Lines       15938    15938           
=======================================
+ Hits        14966    14967    +1     
+ Misses        972      971    -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

nrm = norm(a, p)
if !isempty(a)
aa = copymutable_oftype(a, typeof(first(a)/nrm))
aa = copymutable_oftype(a, float(Base.promote_eltype(a, nrm)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work for arrays of arrays:

julia> a = [[1,2], [3,4]]; nrm = norm(a)
5.477225575051661

julia> Base.promote_eltype(a, nrm)
Any

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of calling __normalize!, which requires us to compute the type of the array, we should just call map, which does the type computation for us. Something like:

function normalize(a::AbstractArray, p::Real = 2)
    nrm = norm(a, p)
    # duplicate logic from __normalize!, but applied to map:
    δ = inv(prevfloat(typemax(nrm)))
    if nrm  δ # Safe to multiply with inverse
        return map(Base.Fix2(*, inv(nrm)), a)
    else # scale elements to avoid overflow
        εδ = eps(one(nrm))/δ
        result = map(Base.Fix2(*, εδ), a)
        return rmul!(result, inv(nrm*εδ))
    end
end

This duplicates the logic in __normalize!, but that seems easier than duplicating the logic in map?

@stevengj
Copy link
Member

stevengj commented Sep 23, 2025

This is both inefficient and doesn't work on arrays that don't support scalar indexing.

first(a) just calls a[first(eachindex(a))] — why does this imply scalar indexing? Any array, even ones that only support cartesian indices, should implement eachindex, no?

Do you have an example of an array type where this fails?

@albertomercurio
Copy link
Contributor Author

For example any GPU vector doesn't support it. The rest of the normalize function is supported.

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.

2 participants