Trouble with function calls

The following commands produce a side effect which I can not explain:

julia> using LinearAlgebra

julia> A = 2*Matrix(I(2))
2×2 Matrix{Int64}:
 2  0
 0  2

julia> B = 3*Matrix(I(2))
2×2 Matrix{Int64}:
 3  0
 0  3

julia> Af = t-> A
#15 (generic function with 1 method)

julia> Af(0)
2×2 Matrix{Int64}:
 2  0
 0  2

julia> Bf = t->B
#17 (generic function with 1 method)

julia> Bf(0)
2×2 Matrix{Int64}:
 3  0
 0  3

julia> C = copyto!(Af(0),Bf(0))
2×2 Matrix{Int64}:
 3  0
 0  3

julia> A
2×2 Matrix{Int64}:
 3  0
 0  3

The result C is correct, but why is the original matrix A changed?

That’s what copyto! does, it writes the elements of one array to another and returns the latter. Af = t -> A is a function that does nothing with its input and accesses the global variable A, then returns the array assigned to A. Bf does the same with global B. As a result, C is assigned to the same array as A is, you can verify with C === A.

1 Like

I encountered this issue in a function, where I believe A and B were not global. I even not used copyto! but another function which overwrites the result. I thought that the value of A is embedded in the function Af and therefore hidden and protected against modifications. I am using a simple fix for the issue

C = copyto!(copy(Af(0)),Bf(0))

which, I admit, is silly but it works.

Still I wonder how the copying involving two numerical matrices Af(0) and Bf(0) can change the value of A which served for the definition of the function Af(t).

This would also mean that the matrix A in the definition of Af(t) can be accessed somehow. Very eager to learn how.

They are, you can verify it:

julia> @code_lowered Af(0)
CodeInfo(
1 ─     return Main.A
)

Af(0) and Bf(0) didn’t do any copying, nor did the C assignment. In the entire example you only had the 2 arrays you started with.

3 Likes

Note that you may want to pay close attention to the scoping rules. The variable does not need to be global to be shared.

julia> function f(A)
           ()->A
       end
f (generic function with 1 method)

julia> g = f([1 0; 0 1])
#1 (generic function with 1 method)

julia> B = g()
2×2 Matrix{Int64}:
 1  0
 0  1

julia> C = g()
2×2 Matrix{Int64}:
 1  0
 0  1

julia> B .= 2
2×2 Matrix{Int64}:
 2  2
 2  2

julia> C
2×2 Matrix{Int64}:
 2  2
 2  2
2 Likes

Thanks for these additional clarifications. I must confess I completely ignored these facts in implementing a number of functions for manipulating periodic matrices in the PeriodicSystems package, which are defined via function mappings.