# How to convert UniformScaling with single entry to Number?

Copying a question from Zulip given that it may be useful in general to discuss in Discourse…

A good trick to perform algebraic operations with both arrays and numbers generically is to use `LinearAlgebra.I` instead of `1`:

``````julia> f(x) = x*I
f (generic function with 1 method)

julia> f(1)
UniformScaling{Int64}
1*I

julia> f(ones(3,3))
3×3 Array{Float64,2}:
1.0  1.0  1.0
1.0  1.0  1.0
1.0  1.0  1.0
``````

The problem I have is that the function `f` is used a lot with simple numbers, and the return type of `f(1)` is not a `Number`. How do we get around this issue? Is there a function in Base that converts a `UniformScaling` with a single entry to a simple `Number`?

If I can provide a simple number in most cases, I will have less issues downstream, and the compiler will likely perform more low-level optimizations?

I know that a `UniformScaling` object `a` with a single entry has the field `a.λ` with the actual number it represents. I wonder how to retrieve this number elegantly, possibly without performance penalties.

maybe you can use `one` depending how much laziness you need:

``````julia> one(3)
1

julia> one(rand(3,3))
3×3 Array{Float64,2}:
1.0  0.0  0.0
0.0  1.0  0.0
0.0  0.0  1.0
``````

or you can add a method to Number (or name it something else):

``````julia> Base.Number(a::UniformScaling) = a.λ

julia> Number(I*3)
3

julia> Number(I*3.0)
3.0
``````
1 Like

How to create a method that works with arrays too? In other words how to dispatch on different kinds of UniformScaling and retrieve the underlying array vs. number?

actually, if your `f` can return array, why does it need to be a `Number`? `UniformScaling` should act as an array in any cases.

1 Like

Because most packages out there fall into this Julia anti-pattern where the code constrains the type of functions to numbers, e.g. `f(x::Number) = ...` prematurely. Also, I am assuming that the compiler can do more magic with the raw numbers in general compared to a more complicated struct.

The code I am referring to is in this PR: https://github.com/JuliaEarth/Variography.jl/pull/33

I would like to be able to do `g = GaussianVariogram() + SphericalVariogram()` and then evaluate the function with `g(1.0)` and get a number.

``````raw(a::UniformScaling) = a.λ
raw(a) = a
``````

to extract the underlying number when we get a uniform scaling object.

Thanks for the help.

Haven’t thought about it for too long but:

``````julia> (f(x::T)::T) where T = x*I
f (generic function with 1 method)

#not sure if this interferes with something else...
julia> Base.convert(::Type{T}, u::UniformScaling{T}) where T<:Number = u.λ

julia> f(1)
1

julia> f(3.0)
3.0

julia> f(rand(2,2))
2×2 Array{Float64,2}:
0.054866  0.471527
0.938986  0.538114

julia> g(x)= x*I
g (generic function with 1 method)

julia> using BenchmarkTools

julia> @btime g(\$1)
0.037 ns (0 allocations: 0 bytes)
UniformScaling{Int64}
1*I

julia> @btime f(\$1)
0.036 ns (0 allocations: 0 bytes)
1

julia> @btime f(\$1.0)
0.037 ns (0 allocations: 0 bytes)
1.0

julia> @btime g(\$1.0)
0.035 ns (0 allocations: 0 bytes)
UniformScaling{Float64}
1.0*I

julia> @btime g(\$(rand(3,3)))
43.347 ns (1 allocation: 160 bytes)
3×3 Array{Float64,2}:
0.543145  0.0292201  0.670174
0.754201  0.290328   0.459809
0.896825  0.678289   0.332216

julia> @btime f(\$(rand(3,3)))
43.347 ns (1 allocation: 160 bytes)
3×3 Array{Float64,2}:
0.32656    0.582854  0.952984
0.0462256  0.589197  0.0384532
0.972349   0.160872  0.807338
``````
1 Like

Nice trick with conversion methods I will follow the simple approach above with a `raw` function to avoid thinking about it too much.

I think that effort is best allocated to fixing these instead of introducing a workaround.

That may not be true, especially if the `struct` just wraps a number.