# Unexpected broadcasting behavior involving eachrow()

**URL:** <https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781>\
**Category:** General Usage\
**Created:** [March 29, 2023, 1:28pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781 "2023-03-29T13:28:26Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [March 30, 2023, 2:38am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/21 "2023-03-30T02:38:07Z")

</div>

I’m perfectly willing to believe that there is some subtlety by which this design was determined to be reasonable, but I just can’t possibly accept the conclusion.

```julia
x = expr

```

Is and will always be interpreted by 99% of people “evaluate `expr` then assign it to the name `x`”, and the whole `.=` shenanigans is Julia for “do this in place” or “broadcast this correctly.”

I would be very surprised if anybody who is not already very familiar with Julia or this specific interaction would ever expect `x .= expr(x)` to behave in the mutating-input way it does, and I know I certainly would expect the RHS to be allocated first before assigning to LHS.

Why shouldn’t this be safe by default? and instead restore the current behavior with

```julia
@fuse x ./= norm.(eachrow(x))

```

It’s just such a sneaky bug. I guarantee there is code out there falling into this trap.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 30, 2023, 2:54am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/22 "2023-03-30T02:54:23Z")

</div>

> [@adienes](#):
>
> I’m perfectly willing to believe that there is some subtlety by which this design was determined to be reasonable

I don’t think it’s subtle at all, syntactic loop fusion has been explained [here](https://julialang.org/blog/2017/01/moredots/). The basic gist is that 1) we can broadcast any operation without having to write boilerplate (like in NumPy via C or Numba), and 2) we can avoid a lot of allocations, and by writing implicit loops in one-liners. And contrary to the examples in this thread, this works out for the most part. Entirely elementwise operations are totally fine, and even reducing operations alone aren’t a problem, it’s only in combination with in-place broadcasting that we have to be careful to allocate sensibly.

> [@adienes](#):
>
> Why shouldn’t this be safe by default?

I’m not arguing that this isn’t an advantage. It’s nice to be saved from aliasing problems automatically. But would people _really_ be happy with their code’s performance degrading, even in cases where aliasing would never be a problem?

I just think that having to manually fuse loops is a lot harder than unfusing loops. Even now, I can write separate or semicolon-delimited lines of code (though I’d need `let` blocks to avoid lingering variables in the global scope), and that provides some clarity to what _isn’t_ in a broadcasting line’s loop. A `@unfuse` macro would just be nice in the very rare case where no adjacent dotted operations are fused.

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [March 30, 2023, 3:05am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/23 "2023-03-30T03:05:10Z")

</div>

I agree with you that

- the default here prefers performance over correctness (as measured by fewer characters to “fix”)
- the behavior has been documented somewhere

But I 100% prefer the numpy approach here where I am never going to be surprised by computations that appear “obvious” in fact doing something totally wrong, and silently so, under the hood.

> this works out for the most part

Ok but when it doesn’t, it’s pretty brutal? If I had written code making this mistake I think it would take me a very very long time to identify, since I would _never_ expect this behavior.

Maybe it’s just a difference in priorities. But I think we both agree that the language gives us tools to do this either 1. guaranteed-alias-free or 2. guaranteed-fusing-high-performance, and to switch behavior requires only a few characters.

I think where we disagree is that you are suggesting 2. should be default, whereas I strongly feel that 1. should be the default.

> implicit loops in one-liners

Snazzy one-liners are fun, but I prefer to have more obvious and predictable (and bug-free) behavior

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [March 30, 2023, 3:10am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/24 "2023-03-30T03:10:41Z")

</div>

> [@adienes](#):
>
> Why shouldn’t this be safe by default?

This is the proposal of the PR. Elsewhere, broadcasting does make some attempt to avoid aliasing surprises.

The downside is that it costs a copy – it ends up the same speed as out-of-place `x ./ norm.(eachrow(x))`. Which is anyway much slower than the best case:

```julia
julia> let x = rand(500, 100)
        @btime $x ./= norm.(eachrow($x)) # with 49182
        @btime $x ./= map(norm, eachrow($x))
        @btime foreach(normalize!, eachrow($x)) # [edit, suggested below]
        @btime $x ./= norm($x; dims=2) # with 43459
       end;
  min 11.594 ms, mean 11.839 ms (2 allocations, 390.67 KiB)
  min 155.416 μs, mean 173.778 μs (1 allocation, 4.06 KiB)
  min 157.542 μs, mean 158.453 μs (0 allocations)
  min 44.375 μs, mean 51.069 μs (5 allocations, 4.14 KiB)

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 30, 2023, 3:11am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/25 "2023-03-30T03:11:55Z")

</div>

> [@adienes](#):
>
> If I had written code making this mistake I think it would take me a very very long time to identify, since I would _never_ expect this behavior.

I think this expectation stems from the comparison between languages, and to be fair the syntax does look similar. But it’s fairly easy to see because I think of Julia’s dots as an easier way of writing loops. Explicit loops are supposedly avoided in performant Python, but when I had to “fuse loops” for performance, I’d be writing those loops in Numba. Even Pythonistas learn to be wary when mutating and iterating the same object in a loop.

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [March 30, 2023, 3:17am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/26 "2023-03-30T03:17:21Z")

</div>

> [@Benny](#):
>
> I think of Julia’s dots as an easier way of writing loops.

I guess I just don’t see Julia dots this way. I much more frequently use them to mean “do my algebra in the correct way on this linalg object.” If I write `r .= Xθ .- y`, yes I technically suppose that’s (mathematically) equivalent to

```julia
for i ∈ eachindex(r)
    r[i] = X[i, :]θ - y[i]
end

```

But there’s no reason to me that thinking of it as a loop should be more natural than as vector subtraction. I agree that one should be careful when mutating an object being looped over, but that’s usually much more obvious when that is happening than the case here. I don’t think it’s just a python thing.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 30, 2023, 3:40am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/27 "2023-03-30T03:40:41Z")

</div>

> [@mcabbott](#):
>
> This is the proposal of the PR. Elsewhere, broadcasting does make some attempt to avoid aliasing surprises.

How does this work anyway? It looks like `mightalias` handles it somehow, but it’s not in the docs, and its docstring doesn’t really explain alias handling in general.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [March 30, 2023, 8:39am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/28 "2023-03-30T08:39:22Z")

</div>

I don’t want to actively enter (even if I follow you with interest) in the “high” part of the discussion.  
Nor, even less, to suggest what might be a “right” solution that satisfies the different needs.  
I just want to report some reflections that may be useful to those who, like me, are not very familiar with the internal aspects of Julia.  
And take comfort from the experts that I’m not completely off track.  
I tried to find a form of the problem that was easy to follow on numbers even “by hand”

At first I thought the problematic expression was equivalent to this, which, in fact, gives no problems.

```julia
   x=[1 2; 3 4]
   x = x .* mean.(eachrow(x))

```

I then realized that instead it is equivalent to this

```julia
   x=[1 3; 2 4]
   x .= x .* mean.(eachrow(x))

```

which, put this way, actually has several problems.  
the first, from what I understand, is that it can’t put real numbers into an array of integers

```julia
julia> x .= x .* mean.(eachrow(x))
ERROR: InexactError: Int64(7.5)

```

the second, actually related to the first, is the unexpected result reported by the OP.

```julia
julia> x=[1. 3; 2 4]
2×2 Matrix{Float64}:
 1.0 3.0
 2.0 4.0

julia> x .= x .* mean.(eachrow(x))
2×2 Matrix{Float64}:
 2.0 7.5
 6.0 20.0

```

Where in the second column one would expect what is obtained from the following expression

Which simultaneously solves the problem of putting real numbers into an matrix of integers.

```julia
  julia> x=[1. 3; 2 4]
2×2 Matrix{Float64}:
 1.0 3.0
 2.0 4.0

julia> x = x .* mean.(eachrow(x))
2×2 Matrix{Float64}:
 2.0 6.0
 6.0 12.0

```

The fact is that in this expression the x on the left side (x= …) is not the same as the x on the right side.  
While in the previous expression (x.=…) the two x’s are the same memory area.  
I’m not sure if that’s completely correct and relevant, but I’d like to show that…

```julia
julia> x=[1. 3; 2 4]
2×2 Matrix{Float64}:
 1.0 3.0
 2.0 4.0

julia> pointer(x)
Ptr{Float64} @0x000001e021f2d840

julia> x .= x .* mean.(eachrow(x))
2×2 Matrix{Float64}:
 2.0 7.5
 6.0 20.0

julia> pointer(x)
Ptr{Float64} @0x000001e021f2d840 # same pointer as above

#--------------

julia> x=[1. 2; 3 4]
2×2 Matrix{Float64}:
 1.0 2.0
 3.0 4.0

julia> pointer(x)
Ptr{Float64} @0x000001e05c1d27a0

julia> x = x .* mean.(eachrow(x))
2×2 Matrix{Float64}:
  1.5 3.0
 10.5 14.0

julia> pointer(x)
Ptr{Float64} @0x000001e05c369e40

```

---

<div class="post-metadata">

**Author:** ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)\
**Post date:** [March 30, 2023, 9:08am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/29 "2023-03-30T09:08:40Z")

</div>

This exact example (but with `sum` not `mean` so that we do not have to depend on `Statistics`) I have in the testset for the PR 😄 [Improve handling of aliasing of AbstractSlices by bkamins · Pull Request #49182 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/49182/files#diff-cf067e25ca3ece9dd0e8855fca7f12563b16348db72c7471a71b3c113f91d229R1831).

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [March 30, 2023, 11:29am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/30 "2023-03-30T11:29:18Z")

</div>

> [@adienes](#):
>
> There’s absolutely no world I want to live in where the above snippet doesn’t normalize the rows’ norms to 1.

I don’t really see the problem here at all. The current behaviour is clearly correct. It’s not _immediately obvious_, so I would probably go wide-eyed for a moment, but after looking closer, it’s more of a facepalm situation. _Of course_ it must behave like that. You are mutating your array _while_ you are calculating properties of it which are used for the mutation step. That should be a huge red flag.

Here’s a better way anyway:

```julia
normalize!.(eachrow(X))

```

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [March 30, 2023, 11:32am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/31 "2023-03-30T11:32:25Z")

</div>

the problem is the implication of parenthesis on the ordering of computations _ **differs** _ from all other 30+ years of programming experience!

that said, in `Julia` (and _only_ in `Julia`), whenever `.` broadcasting is used, we can _ **not** _ rely on the parenthesis to direct the order of computations. Instead, _ **separate** _ computations should be _ **explicitly** _ put in suitable order. Single line broadcasting should be _ **avoided** _ for clarity.

---

<div class="post-metadata">

**Author:** ![tomtom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomtom/32/5106_2.png) [@tomtom](https://discourse.julialang.org/u/tomtom)\
**Post date:** [March 30, 2023, 11:58am UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/32 "2023-03-30T11:58:11Z")

</div>

to clarify what’s the “parenthesis” and “order of computation” things, let’s consider:

case 1:

```julia
y = reshape(1.0:6, 3, 2);
julia> y .*= 1 ./ LinearAlgebra.norm.(eachrow(y))
3×2 Matrix{Float64}:
 0.242536 0.998167
 0.371391 0.997253
 0.447214 0.997234

```

case 2:

```julia
y = reshape(1.0:6, 3, 2);
julia> y .*= ( 1 ./ LinearAlgebra.norm.(eachrow(y)) ) # with ( )
3×2 Matrix{Float64}:
 0.242536 0.998167
 0.371391 0.997253
 0.447214 0.997234

```

case 1 & 2 get the same result!? That means the extra **( )** used in case 2, which intend to compute things inside **first** , are _ **ignored, overrided** _!!!

Don’t we see it’s a _problem_?

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [March 30, 2023, 12:40pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/33 "2023-03-30T12:40:40Z")

</div>

`normalize!.(eachrow(X))`

It’s also horrifying that doesn’t work 😬 . What’s the point of having lovely generic and composable functions like `normalize!` and `eachrow` if we can’t compose them without fear that it’s doing something silently wrong?

This is not just a familiarity thing. My eyes did go wide when I first read the OP, but they went wider when I saw people trying to justify the interaction. I do now understand the mechanism that causes this behavior to arise, but I feel so strongly that it should be treated as an _urgent bugfix_ that I’m not sure what else to say.

I would put pretty good money on the claim that there is at least one major package (or Base/stdlib) that makes this mistake somewhere. I agree with @tomtom. This is not just a regular language quirk; it breaks established convention across any language I’ve ever used.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [March 30, 2023, 12:46pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/34 "2023-03-30T12:46:23Z")

</div>

> [@adienes](#):
>
> I guess I just don’t see Julia dots this way. I much more frequently use them to mean “do my algebra in the correct way on this linalg object.”

I haven’t read this entire thread, but this rule that you propose is far too vague for the language to use. It’s an example of “Do what I mean, not what I say.” The language has to adopt a more formal definition of what in-place broadcasting means. Furthermore, how are we supposed to reason about broadcasting code by the rule “do my algebra in the correct way on this object”? It’s just not a coherent API.

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [March 30, 2023, 12:54pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/35 "2023-03-30T12:54:10Z")

</div>

> [@CameronBieganek](#):
>
> The language has to adopt a more formal definition of what in-place broadcasting means. Furthermore, how are we supposed to reason about broadcasting code by the rule “do my algebra in the correct way on this object”? It’s just not a coherent API.

“`.=` must allocate the RHS before broadcast-assigning to LHS unless the user explicitly opts into fusion” seems coherent and specific

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [March 30, 2023, 12:54pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/36 "2023-03-30T12:54:56Z")

</div>

> [@adienes](#):
>
> This is not just a regular language quirk; it breaks established convention across any language I’ve ever used.

How many languages have you used with an in-place broadcasting syntax?

> [@adienes](#):
>
> `normalize!.(eachrow(X))`
> 
> It’s also horrifying that doesn’t work 😬 .

Seems to work for me:

```julia
julia> A = [1.0 2.0
            3.0 4.0];

julia> normalize!.(eachrow(A));

julia> norm.(eachrow(A))
2-element Vector{Float64}:
 0.9999999999999999
 1.0

```

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [March 30, 2023, 12:56pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/37 "2023-03-30T12:56:01Z")

</div>

> [@adienes](#):
>
> “`.=` must allocate the RHS before broadcast-assigning to LHS unless the user explicitly opts into fusion” seems coherent and specific

But that’s exactly what _in-place_ broadcasting is trying to avoid. It’s trying to avoid temporary array allocations!

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [March 30, 2023, 12:59pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/38 "2023-03-30T12:59:03Z")

</div>

> [@adienes](#):
>
> It’s also horrifying that doesn’t work 😬 .

My point was that it works, and is also clearer. What did I miss?

> [@adienes](#):
>
> “`.=` must allocate the RHS before broadcast-assigning to LHS unless the user explicitly opts into fusion”

The user did explicitly opt into fusion. And you get your desired behaviour, trivially, by using `=` instead of `.=`

Why force `.=` to behave like `=`, that ruins the point of it.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 30, 2023, 1:17pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/39 "2023-03-30T13:17:45Z")

</div>

> [@tomtom](#):
>
> case 1 & 2 get the same result!? That means the extra **( )** used in case 2, which intend to compute things inside **first** , are _ **ignored, overrided** _!!!
> 
> Don’t we see it’s a _problem_?

You can check the order of operations in `Meta.@lower y .*= 1 ./ LinearAlgebra.norm.(eachrow(y))`. The operations on the right side are already getting computed first per iteration.

Even in simpler cases without broadcasting, updating operators are expected to occur last:

```julia
julia> x = 2; x *= x + 10 ##
24
julia> x = 2; x = x * x + 10 #
14
julia> x = 2; x = x * (x + 10) ##
24
julia> x = 2; (x *= x) + 10 #
14

```

To be fair, that’s not documented in the [Updating Operators](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Updating-operators) section right now. But also to be fair, assignment and updating operators’ precedence being the lowest [is documented](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity), and this behavior is present in other languages too.

---

<div class="post-metadata">

**Author:** ![adienes](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/adienes/32/37459_2.png) [@adienes](https://discourse.julialang.org/u/adienes)\
**Post date:** [March 30, 2023, 1:31pm UTC](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781/40 "2023-03-30T13:31:26Z")

</div>

> [@DNF](#):
>
> My point was that it works, and is also clearer. What did I miss?

My mistake, I had thought you were implying it was also broken, but I didn’t actually try it. I retract the statement 🙂

> [@DNF](#):
>
> And you get your desired behaviour, trivially, by using `=` instead of `.=`

well, no, no I don’t, if my desired behavior was the “in-place” aspect. if I write a function

```julia
normalizerows!(x)
    x .= normalize.(eachrow(x))
end

```

Then it will be broken. Obviously this is a bad implementation but it should still _work_.

One of the biggest problems for me is that this violates transitivity with `=`, in that

```julia
x ./= norm.(eachrow(x))

```

Means something different than

```julia
val = norm.(eachrow(x))
x ./= val

```

Transitivity of assignment seems like a pretty inviolable principle to me. Anyway, it’s seeming unlikely we will convince each other this way, so I’ll stop being inflammatory. Let me take a little bit to collect my thoughts and maybe I can come back with a new angle

[Previous page](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781.md?page=1)

[Next page](https://discourse.julialang.org/t/unexpected-broadcasting-behavior-involving-eachrow/96781.md?page=3)
