Search code examples
julialinear-algebra

Julia Dot Broadcast and 1 / Vector


I came across a case that I do not understand while working with Julia: 1. / vs 1 ./

The second and third methods produces what I expect, while the first one does not. I can also see that the ratios are inverted (i.e. result1[3] / result[1] = 4.0 while result2[3] / result2[1] = 0.25).

I am just curious what the difference is. I know this is dot broadcasting, but I was not able to get any traction about this situation.

Δx = [2.0, 4.0, 8.0]
result1 = 1. / Δx
result2 = 1 ./ Δx
result3 = 1. ./ Δx

println(result1)  # Output: [0.023809523809523808 0.047619047619047616 0.09523809523809523]
println(result2)  # Output: [0.5, 0.25, 0.125]
println(result3)  # Output: [0.5, 0.25, 0.125]

println(result1[3] / result1[1])    # Output: 4.0
println(result2[3] / result2[1])    # Output: 0.25

Update:

result1 = 1. /[2.0, 4.0, 8.0]

# Output:
# 1×3 transpose(::Vector{Float64}) with eltype Float64:
#  0.0238095  0.047619  0.0952381

result2 = 1 ./[2.0, 4.0, 8.0]
# Output:
# 3-element Vector{Float64}:
#  0.5
#  0.25
#  0.125

Solution

  • (Note: I tested this on Julia 1.9.4 on Windows)

    The expressions 1. / and 1 ./ are different in that the first tokenizes to 1. and / while the second tokenizes to 1 and ./.

    1 and 1. are numbers. The former is an integer, the latter is a floating point number.

    The ./ operator, because of the . in front of it, is a broadcast operation, i.e. / applied to each element of something. 1 ./ [1,2,3] is equivalent to [1/1, 1/2, 1/3].

    The / operator is usually division. On some types it is defined to be something else.

    In Julia, lots of mathy things are defined to have mathy operators that operate on the entire object, not its elements. Example: * on matrices causes matrix multiplication, not elementwise multiplication (use .* for that).

    Investigation with @code_lowered 1 / [1, 2, 3] reveals that the pseudo-inverse (LinearAlgebra.pinv) of the vector is calculated.

    julia> @code_lowered 1 / [1, 2, 3]
    CodeInfo(
    1 ─ %1 = LinearAlgebra.pinv(v)
    │   %2 = x * %1
    └──      return %2
    )
    

    Antonello and Oscar Smith have pointed out (see comments) that this specific behavior has been discussed and is likely to be removed in the future.