# How to update a symmetric matrix respecting API boundaries?

**URL:** https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246
**Category:** General Usage
**Tags:** question, linearalgebra, symmetric
**Created:** [September 10, 2025, 7:35am UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246 "2025-09-10T07:35:32Z")
**Posts on this page:** 9
**Page:** 1

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [September 10, 2025, 7:35am UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/1 "2025-09-10T07:35:32Z")

</div>

Suppose I have a symmetric matrix `H`, and I would like to update it in place. For the purposes of this MWE, the update is adding `f(i,j)` to elements, based on index.

How can I do this while respecting API boundaries? `setindex!` only allows setting diagonals for symmetric matrices (I am not sure why). I could get the `parent`, but `.uplo` is not exposed by `LinearAlgebra`, if it was, I could do this:

```julia
Z = parent(H)
n = size(Z, 1)
if H.uplo == 'U'
    for j in 1:n
        for i in 1:j
            Z[i, j] += f(i, j)
        end
    end
else
    for j in 1:n
        for i in j:n
            Z[i, j] += f(i, j)
        end
    end
end

```

---

<div class="post-metadata">

### Author: ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)
#### Post date: [September 10, 2025, 8:41am UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/2 "2025-09-10T08:41:55Z")

</div>

```julia
Z = parent(H)
n = size(Z, 1)
for j in 1:n, i in 1:n
    Z[i, j] += f(i, j)
end

```

It’s stupid but it’s the only way. The real answer is of course to not respect API boundaries.

EDIT: On a second thought, you can do this

```julia
function find_uplo(M)
    temp12 = M[1,2]
    temp21 = M[2,1]
    P = parent(M)
    P[1,2] = zero(P[1,2])
    P[2,1] = one(P[2,1])
    if M[1,2] == P[1,2]
        P[1,2] = temp12
        return 'U'
    else
        P[2,1] = temp21
        return 'L'
    end
end

```

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [September 10, 2025, 10:04am UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/3 "2025-09-10T10:04:07Z")

</div>

I see the following general solutions to fix this:

1. allow `setindex!` to work off-diagnal for `Symmetric`. The idea is that it automatically does the right thing, eg if the inner representation is upper, and you are trying to set the lower triangle, it just exchanges indices. Cheap (just checking a conditional), convenient, hassle free, does not expose the implementation. (It is understood that if the parent is immutable, this would error). Disadvantage: access pattern may not be optimal (rows vs columns).
2. Exposing `.uplo` as part of the API.
3. Combination of both: `setindex!` always does the right thing, but you can make it optimal by conditioning on which triangle stores stuff.

---

<div class="post-metadata">

### Author: ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)
#### Post date: [September 10, 2025, 10:24am UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/4 "2025-09-10T10:24:14Z")

</div>

something I have been missing occasionally is a way to iterate the _structural_ indices in an efficient way. For

- Symmetric this would be the indices of the upper/lower triangles (in the most performant order)
- Diagonal, it would be equivalent to `diagind`
- SparseMatrixCSC it would be equivalent to the nested loops I always need to [look up again in the docs of `nzrange`](https://docs.julialang.org/en/v1/stdlib/SparseArrays/#SparseArrays.nzrange)

Such a mechanism in combination with extending `setindex!` to offdiagonal indices for `Symmetric` would be a nice generic interface for this usecase.

---

<div class="post-metadata">

### Author: ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)
#### Post date: [September 11, 2025, 10:44am UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/5 "2025-09-11T10:44:18Z")

</div>

> [@abraemer](#):
>
> iterate the _structural_ indices in an efficient way

I think that this would be great to experiment with in a package (that would support relevant types in `Base` and the standard libraries, and allow other packages to hook into the API).

First I thought that _nonzero_ (sparse, diagonal) and _redundant_ (eg `Symmetric`) indices would need different handling, but I think that an iterator that would go through all indices that

1. are not redundant, _and_
2. you are allowed to change

would be enough.

---

<div class="post-metadata">

### Author: ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)
#### Post date: [September 11, 2025, 1:56pm UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/6 "2025-09-11T13:56:07Z")

</div>

`setindex!` for off-diagonals isn’t supported for a `Symmetric` since `A[i,j]` and `A[j,i]` are linked, and `setindex!` is expected to modify only one index and not both. I think exposing `uplo` is a better idea, since that allows one to wrap the parent in a triangular matrix and modify the off-diagonal elements. This is a well-defined `setindex!` operation.

```julia-auto
julia> S = Symmetric([1 2; 3 4])
2×2 Symmetric{Int64, Matrix{Int64}}:
 1 2
 2 4

julia> U = UpperTriangular(parent(S))
2×2 UpperTriangular{Int64, Matrix{Int64}}:
 1 2
 ⋅ 4

julia> U[1,2] = 10
10

julia> S
2×2 Symmetric{Int64, Matrix{Int64}}:
  1 10
 10 4

```

---

<div class="post-metadata">

### Author: ![araujoms](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/araujoms/32/217734_2.png) [@araujoms](https://discourse.julialang.org/u/araujoms)
#### Post date: [September 11, 2025, 3:03pm UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/7 "2025-09-11T15:03:11Z")

</div>

I think

```julia
function Base.setindex!(A::Symmetric, val, i::Integer, j::Integer)
           if (i < j && A.uplo == 'L') || (i > j && A.uplo == 'U')
               setindex!(A.data, val, j, i)
           else
               setindex!(A.data, val, i, j)
           end
           return A
       end

```

is a perfectly sensible interface, if you didn’t want the matrix to stay symmetric you shouldn’t have wrapped it with `Symmetric`.

The problem with it is that it will silently send performance to hell, so it is indeed better to expose `uplo` and let the user handle indexing.

---

<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: [September 11, 2025, 3:26pm UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/8 "2025-09-11T15:26:57Z")

</div>

The issue with reindexing `[i,j]` to `[j,i]` when accessing the wrong triangle is that there are a lot of sensible operations that become easy to mess up. For example, `X::Symmetric .+= Y` would (without a new specialized implementation) end up adding `Y` twice to each nondiagonal.

We could try to make specializations that handle these, but I’m not certain we could find something much more sensible or ergonomic than determining the `uplo` and then modifying just the relevant triangle of the parent. While there are a lot of simple cases where the correct thing would be obvious and trivial, such features usually die under the scrutiny of complex or edge cases.

Without a new interface, I don’t think I’d go much further than to allow `setindex!` to the “live” triangle and throw a bounds error if accessing the “ghost” half. A new interface might include functions like `setindexsym!`(with the semantics of setting both `[i,j]` and `[j,i]`, regardless of matrix type), but at this point it’s still not much better than a manual implementation (assuming a public `uplo`-getter).

Up to now, if I will do mutations I will tend not to wrap my arrays in `Symmetric` except at the call sites of functions where the wrapper is relevant.

---

<div class="post-metadata">

### Author: ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)
#### Post date: [September 11, 2025, 4:23pm UTC](https://discourse.julialang.org/t/how-to-update-a-symmetric-matrix-respecting-api-boundaries/132246/9 "2025-09-11T16:23:22Z")

</div>

> <https://github.com/JuliaLang/LinearAlgebra.jl/pull/1440>
>
> This lets one access the \`uplo\` field of a \`Symmetric\`/\`Hermitian\` matrix withou…t accessing the internal fields of the struct.
