How to give an one-time assignment to a large parametric matrix?

One-time assignment means something essentially equal to this:

function Mf(t)
    M=[t t^2;t^3 t^4]    #assignment, just list all its entries in the bracket
end

but not this:

function Mf(t)
    M=zeros(2,2)    #assignment
    M[1,1]=t    #mutation
    M[1,2]=t^2
    M[2,1]=t^3
    M[2,2]=t^4
    M
end

When a large parametric matrix(e.g. M(t) is 1000x1000) is needed, I can’t assign it by the first way(list all 1000x1000 entries), but to use the second way

function Mf(t)
    M=zeros(1000,1000)    #assignment
    for i=1:1000
        for j=1:1000
            M[i,j]=m_ij(t)      #mutation, m_ij(t) is an explicit expression depends on i,j,t
        end
    end
    M
end

However, if I need to compute gradient of a loss function of M(t) by Zygote, e.g. L(t)=det(M(t))

gradient(t->det(Mf(t)), t0)[1]

an error will occur since mutating array in differetiated function is not allowed, means that I shouldn’t adopt the second way

ERROR: LoadError: Mutating arrays is not supported -- called setindex!(::Matrix{Float64}, _...)

So I wonder if there is a method of assigning a large parametric matrix at one-time without mutating it (the explicit expression of m_ij(t) is known)
I failed to find the answer by searching and watching tutorials. I would be grateful if someone tells the solution.

You can do this, though I don’t know if that solves the specific error you are facing:

julia> M = [ exp(i*j) for i in 1:10, j in 1:10 ]
10×10 Matrix{Float64}:
     2.71828      7.38906      20.0855        54.5982      …  1096.63        2980.96        8103.08        22026.5
     7.38906     54.5982      403.429       2980.96              1.2026e6       8.88611e6      6.566e7         4.85165e8
    20.0855     403.429      8103.08           1.62755e5         1.31882e9      2.64891e10     5.32048e11      1.06865e13
    54.5982    2980.96          1.62755e5      8.88611e6         1.44626e12     7.8963e13      4.31123e15      2.35385e17
   148.413    22026.5           3.26902e6      4.85165e8         1.58601e15     2.35385e17     3.49343e19      5.18471e21
   403.429        1.62755e5     6.566e7        2.64891e10  …     1.73927e18     7.01674e20     2.83075e23      1.14201e26
  1096.63         1.2026e6      1.31882e9      1.44626e12        1.90735e21     2.09166e24     2.29378e27      2.51544e30
  2980.96         8.88611e6     2.64891e10     7.8963e13         2.09166e24     6.23515e27     1.85867e31      5.54062e34
  8103.08         6.566e7       5.32048e11     4.31123e15        2.29378e27     1.85867e31     1.5061e35       1.2204e39
 22026.5          4.85165e8     1.06865e13     2.35385e17        2.51544e30     5.54062e34     1.2204e39       2.68812e43


1 Like

I ignore the actual matrix types involved. Would the following work for you?
The overhead of one-time allocation should be amortized over the full run.

  • generate a first matrix with your mutating method
  • create a second, non-mutating matrix as (deep) copy
  • empty your first matrix
  • use the second one in Zygote

This actually working, thanks sir!

using Zygote

function Mf(t)
    M=[t^(2i+j-2) for i in 1:2, j in 1:2]
end

gradient(t->sum(Mf(t)),2)[1]
2 Likes

I am not sure if you mean to do this

using Zygote

function Nf(t)
    N=zeros(2,2)
    N[1,1]=t
    N[1,2]=t^2
    N[2,1]=t^3
    N[2,2]=t^4
    N2=deepcopy(N)
end

gradient(t->sum(Nf(t)),2)[1]

this code reports the same error

My answer tried to provide a parametrically built, immutable matrix.
You actually want to pass a function generating a parameterized matrix, as did @lmiq .

1 Like