Copying mutable structs without using deepcopy()

Dear colleagues,

I am working on an optimization problem where I have to group a permutation of jobs, which have some characteristics. A solution to this problem can be easily represented as a vector of vectors of a type Batch, where Batch is a mutable struct defined as follows:

mutable struct Batch
    Jobs::Vector{Int16}
    Size::Int16
    Family::Int8

    Batch() = new(Vector{Int16}[], 0, 0)
end

Therefore, a solution is represented as:

sol = Vector{Batch}[[] for i::Int8 in 1:m]

where m is a given parameter.

I have also a reference solution called sol_ref which is also a vector of vectors of type batch and size m. The inner vectors are allowed to have a different size.

During the execution of my program I often have to replace sol with sol_ref and vice-versa. However, I want to avoid using deepcopy(), since it is memory intensive. My idea was to delete all elements manually and then replace the inner elements of each vector iteratively:

function Replace_Solution(sol::Vector{Vector{Batch}}, sol_ref::Vector{Vector{Batch}}, m::Int8)
    #----- Replaces the current solution with new ones

    for i in 1:m
        empty!(sol[i])
        append!(sol[i], sol_ref[i])
    end
end

However, Replace_Solution often leads to errors since it is not guaranteeing separate memory allocations. I am looking for ways to efficiently update sol with the contents of sol_ref. Can anyone give my a perspective of why this is happening?

OBS: When I use deepcopy(), the algorithm runs fine.

1 Like

The issue is that the the mutable structures will copied (when using append!) by reference, such that they will reference the same structures (note that even if the structure was not mutable, having the mutable Jobs field in it would cause you issues of the same sort). Thus, you need to copy the values explicitly for all elements of the structure One way to do it is to define a function to copy the data between two instances of Batch, and then use that. For example, something like:

julia> function copy_data!(target::Batch, source::Batch)
           target.Size = source.Size
           target.Family = source.Family
           target.Jobs .= source.Jobs
           return target
       end
copy_data! (generic function with 1 method)

to be used with:

julia> function Replace_Solution!(sol::Vector{Vector{Batch}}, sol_ref::Vector{Vector{Batch}}, m)
           for i in eachindex(sol,sol_ref)
               copy_data!(sol[i], sol_ref[i])
           end
           return sol
       end
Replace_Solution (generic function with 1 method)

I wouldn’t call the function copy_ if it doesn’t copy.

It does copy the values of the fields, in the same sense that x = 1; y = copy(x) copies. But that’s why I named it copy_data!.

Does this “just work”?

help?> copy!
search: copy! copyto! circcopy! unsafe_copyto! replaceproperty! copy copysign

  copy!(dst, src) -> dst


  In-place copy of src into dst, discarding any pre-existing elements in dst.
  If dst and src are of the same type, dst == src should hold after the call.
  If dst and src are multidimensional arrays, they must have equal axes.

  See also copyto!.

  │ Julia 1.1
  │
  │  This method requires at least Julia 1.1. In Julia 1.0 this method
  │  is available from the Future standard library as Future.copy!.
1 Like