I noticed that the absence of many in-place implementations of most functions, like apply(MPO,MPS)
, If I’m performing an evolution of the state in time it would be very useful to have a function that modifies the MPS in-place. Also the factorizations aren’t implemented in-place, even though it would be the most natural think to do in a mps, and LAPACK functions naturally supports the overwriting of the initial matrix. Is there a particular reason that has led to don’t implement this in-place functions?
Often in MPS calculations (such as in apply
) we dynamically adjust the bond dimension based on the truncation/entanglement, so it isn’t so easy to reuse the data that was input. In principle it would be possible, for example by pre-allocating a large chunk of memory (say enough that we think the calculation won’t exceed that amount) and overwriting it during the calculation, but that is a bit more advanced to implement properly. It is definitely something we have in mind to try at some point. Some Julia packages like GitHub - MasonProtter/Bumper.jl: Bring Your Own Stack may be beneficial for that.
There are cases where we know the dimension or that the size doesn’t change, like when we perform a truncation, we first left orthogonalize the MPS, but orthogonalize!
uses factorization
which is going to perform a QR
decomposition, but in such case we already know that the size of each tensor is not going to change so why don’t us a qr!
implementation?
In apply
is impossible to know the size beforehand, but, taking for example the naive
implementation, by changing
ψ_out = typeof(ψ)(N)
for j in 1:N
ψ_out[j] = A[j] * ψ[j]
To
update!(ψ[j], A[j] * ψ[j]) # Overwrite the location of ψ[j]
where
function update!(ψ_old::ITensor, ψ_new::ITensor)
setstorage!(ψ_old, storage(ψ_new))
setinds!(ψ_old, inds(ψ_new))
end
(Probably already ψ[j]=A[j] * ψ[j]
is enough) would make the old location of \psi[j] unreachable so that the GC could collect that memory already on exit from the function