# How can I make an in-place swap function?

**URL:** <https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489>\
**Category:** General Usage\
**Tags:** question, broadcast, array\
**Created:** [August 4, 2023, 2:14pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489 "2023-08-04T14:14:18Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [August 4, 2023, 2:14pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/1 "2023-08-04T14:14:18Z")

</div>

Suppose I have two arrays `A, B` of different sizes. Then `A .+ B` broadcasts them to a common size (if possible) and sums them. Suppose `A` here is “larger” (in the sense that `all(size(B) .== size(A) .|| size(B) .== 1)` is true).

Given an index `i` of `A` (`A[i]` ), how can I compute `j` such that `B[j]` gives the element of `B` that would be summed to `A[i]` in the broadcasted expression?

My motivation for this question, is that I am trying to code an in-place version of the following function:

```julia
function swap(conditions::Union{AbstractArray{Bool}, Bool}, x, y)
    x_new = ifelse.(conditions, y, x)
    y_new = ifelse.(conditions, x, y)
    return x_new, y_new
end

```

The following:

```julia
function swap!(x::AbstractArray, y::AbstractArray, conditions::AbstractArray{Bool})
    if size(x) == size(y) == size(conditions)
        for i in eachindex(x, y, conditions)
            if conditions[i]
                x[i], y[i] = y[i], x[i]
            else
                x[i], y[i] = x[i], y[i]
            end
        end
        return x, y
    else
        throw(DimensionMismatch("x, y and conditions must have the same size"))
    end
end

```

works if x, y, conditions are of the same size. I would like to relax that, requiring that `x,y` be of the same size, but `conditions` need only be “broadcastable” to a matching size.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [August 4, 2023, 2:37pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/2 "2023-08-04T14:37:56Z")

</div>

Hm good question, the closest I could come up with quickly was this, but it still allocates an array of `nothing`s which is not nice 🙂

```julia
function swap!(x, y, conditions)
   broadcast(CartesianIndices(x), conditions) do i, c
       if c
           x[i], y[i] = y[i], x[i]
       end
   end
   return
end

```

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [August 4, 2023, 2:47pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/3 "2023-08-04T14:47:20Z")

</div>

Maybe a `FillArray` could help, though I’m not sure how.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [August 4, 2023, 2:55pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/4 "2023-08-04T14:55:54Z")

</div>

I found some broadcasting internals that I could coax into doing this. Maybe there’s a different more obvious way 🙂

```julia
julia> function swap!(x, y, conditions)
           b = Broadcast.Broadcasted(tuple, (CartesianIndices(x), conditions))
           for (i, c) in b
               if c
                   x[i], y[i] = y[i], x[i]
               end
           end
           return
       end
swap! (generic function with 2 methods)

julia> x = fill(1, 3, 5); y = fill(2, 3, 5); conditions = [true, false, true, false, true]';

julia> swap!(x, y, conditions)

julia> x
3×5 Matrix{Int64}:
 2 1 2 1 2
 2 1 2 1 2
 2 1 2 1 2

julia> y
3×5 Matrix{Int64}:
 1 2 1 2 1
 1 2 1 2 1
 1 2 1 2 1

julia> @time swap!(x, y, conditions)
  0.000012 seconds

```

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [August 4, 2023, 3:19pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/5 "2023-08-04T15:19:28Z")

</div>

Here’s what I came up with.The first uses a `view` into `conditions` that repeatedly samples the dimensions that need broadcasting. The second figures out which dimensions need to be broadcast, then adjusts the index into `conditions` accordingly.

```julia
function swapif1!(x,y,cond)
	axes(x) == axes(y) || error("x and y must have the same axes")
	all(d -> size(cond,d) == 1 || axes(cond,d) == axes(x,d), ndims(x)) || error("could not broadcast cond to size of x, y")
	axc = ntuple(d -> ifelse(size(cond,d) == 1, StepRangeLen(firstindex(cond,d),0,size(x,d)), :), Val(ndims(x)))
	vcond = view(cond,axc...)
	for i in eachindex(x,y,vcond)
		if vcond[i]
			x[i],y[i] = y[i],x[i]
		end
	end
	return x,y
end

function swapif2!(x,y,cond)
	axes(x) == axes(y) || error("x and y must have the same axes")
	all(d -> size(cond,d) == 1 || axes(cond,d) == axes(x,d), ndims(x)) || error("could not broadcast cond to size of x, y")
	szc1 = ntuple(d -> size(cond,d) == 1, Val(ndims(x))) # find axes where size(cond,ax) == 1
	indc1 = ntuple(d -> firstindex(cond,d), Val(ndims(x))) # valid index for c on axes of size 1
	for i in eachindex(IndexCartesian(), x, y)
		ic = CartesianIndex(ifelse.(szc1, indc1, Tuple(i))...)
		if cond[ic]
			x[i],y[i] = y[i],x[i]
		end
	end
	return x,y
end

```

The first one ends up allocating a lot. I didn’t look into why, but there’s probably a type instability somewhere. So I’ll recommend the second.

But I think I like the suggestion above that uses tools from `Broadcast` to handle this. That seems clean.

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [August 4, 2023, 4:13pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/6 "2023-08-04T16:13:47Z")

</div>

Very elegant, thanks!

> [@jules](#):
>
> `Broadcast.Broadcasted`

This is not public API, though, I think?

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [August 4, 2023, 6:32pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/7 "2023-08-04T18:32:12Z")

</div>

Not sure. Not everything considered public/stable needs to be exported I think.

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [August 4, 2023, 7:09pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/8 "2023-08-04T19:09:28Z")

</div>

It looks like `Broadcasted` appears [in the docs here](https://docs.julialang.org/en/v1/manual/interfaces/#man-interfaces-broadcasting). While it is not documented formally and explicitly, it is documented as part of the broadcasting interface. It is intended to be used to extend the broadcast machinery, so (in my reading) it is part of the broadcasting API and thus part of the Julia API, even though it is not exported.

However, I would use

```julia
Broadcast.broadcasted(tuple, CartesianIndices(x), conditions)

```

rather than

```julia
Broadcast.Broadcasted(tuple, (CartesianIndices(x), conditions))

```

The former produces the latter, but appears to be the more standard interface. Notice

```julia
julia> @code_lowered tuple.(CartesianIndices(x), conditions)
CodeInfo(
1 ─ %1 = Base.broadcasted(Main.tuple, x1, x2)
│ %2 = Base.materialize(%1)
└── return %2
)

```

So what is being done here is equivalent to the `broadcasted` version, except that a normal call with dots also calls `materialize` to collect the result to a variable. But the non-`materialize`d version can still be iterated and that’s why it works in this `for` loop. `Broadcast.broadcasted` can also be useful if you want to assemble a broadcasted expression in multiple parts or over multiple lines without materializing the intermediate results.

---

<div class="post-metadata">

**Author:** ![e3c6](https://avatars.discourse-cdn.com/v4/letter/e/e79b87/32.png) [@e3c6](https://discourse.julialang.org/u/e3c6)\
**Post date:** [August 4, 2023, 7:40pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/9 "2023-08-04T19:40:33Z")

</div>

Good point. So I’m writing it like this now:

```julia
function swap!(conditions::Union{AbstractArray{Bool}, Bool}, x::AbstractArray, y::AbstractArray)
    if size(x) == size(y)
        b = broadcasted(tuple, CartesianIndices(x), CartesianIndices(y), conditions)
        for (ix, iy, c) in b
            if c
                x[ix], y[iy] = y[ix], x[iy]
            end
        end
        return x, y
    else
        throw(DimensionMismatch("x, y must have the same size"))
    end
end

```

I am using different indices `ix, iy` in case `x, y` have different indexing patterns (though not sure this is the best approach).

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 4, 2023, 8:30pm UTC](https://discourse.julialang.org/t/how-can-i-make-an-in-place-swap-function/102489/10 "2023-08-04T20:30:58Z")

</div>

> [@e3c6](#):
>
> would like to relax that, requiring that `x,y` be of the same size, but `conditions` need only be “broadcastable” to a matching size.

You could just use ordinary broacasting via [StructArrays.jl](https://github.com/JuliaArrays/StructArrays.jl):

```julia
using StructArrays
swap(condition::Bool, (x, y)) = condition ? (y, x) : (x, y)
function swap!(x::AbstractArray, y::AbstractArray, conditions)
    a = StructArray((x, y))
    a .= swap.(conditions, a)
    return x, y
end

```
