I’m trying to design a type MyType having a certain number of fields amongst which matrix::AbstractMatrix. In production MyType is intended to have its field matrix modified (hundreds times a day)… I’ve encountered some difficulties in building MyType’s updaters resizing/reshapingmatrix.
some constraints: MyType should be immutable. matrix should be a 2-d array.
Questions
Is there a way to resize!(matrix) (empty, update…)?
If not, I thought about building the field matrix as a reshaped view on another MyType’s field/property, say kern::AbstractVector for which operations like resize! are more “natural” so that genuinely built updaters on kern will lead matrix (the rehaped view of kern) reflect those changes. Is there a way to work around with this idea? A better idea? What are some drawbacks regarding performance etc. ? Should I just let MyType be a mutable struct and move on?
A field of a strictly immutable composite type does not need to reference other immutables, and you can make mutation API forward to that field. That’s how many wrappers of mutable arrays work. A mutable composite type would only add the ability to reassign a different matrix to the field, which is not mutation of the matrix and is likely more expensive.
The real problem is that matrices, or rather any array that isn’t a vector, aren’t straightforwardly resizable. Some examples:
push!-ing 1 element to a MxN matrix where N != 1 can’t result in a matrix
resize!-ing an MxN matrix to P elements isn’t guaranteed to result in a matrix
resize!-ing an MxN matrix to a PxQ matrix has no unique result and would need to copy around most of the elements in general
A matrix type that supports more resizing is in ElasticArrays.jl, but that only supports resizing along the last dimension to avoid those issues.
Thank you for your reply.
Please, precize what you mean by “…and you can make mutation API forward to that field.” is there a way to make a specific field mutable?
By the way, you say that viewing another field is not a good idea?
No sir, I do not have any limit. It can grow to any size… but in practice the number of columns will unlikely go above 40.
It’s the number of rows that can go to the thousands…
Though my updaters are there to ensure that sizes/data are correct.
Yes, but read on. Whenever you create a matrix, the system allocates you a block of memory for storing this matrix. The reason why you can’t resize!(matrix) is because there is other data near this piece of memory, and easily expanding this matrix might overwrite important data in the surrounding memory. But shrinking this matrix is possible. So if you know the maximum size of the matrix, you can preallocate a maximum block of memory to store it. The following code gives you the freedom to change the size of the matrix with 0 cost:
using Buffers
buffer1 = MAllocBuffer(100 * 100); # preallocate 100*100 momery
A = alloc!(buffer1, 10, 10) # a 10*10 matrix `A`
# then change the size of `A`.
drop!(buffer, A)
A = alloc!(buffer1, 50, 60)
However, according to your description, the change in the size of that array is very dynamic and can get very large. So, preallocation is not a good idea. There are still two ways to do this:
create your MyType as a mutable struct and reallocate the matrix each time its size changes, this method is best suited to your dynamic size characteristics.
use a buffer that automatically expands when you run out of buffers, for example:
using Buffers
buffer = Buffer(100)
A = alloc!(buffer, 10, 10)
drop!(buffer, A)
A = alloc!(buffer, 100, 100)
Note that alloc!(buffer, 10, 10) returns a matrix view, which makes matrix A slightly slower to compute than MAllocBuffer. It’s best to actually test which method faster.
But I think both methods are definitely faster than the second point you made, which is to create a vector and view it as a matrix and engage the operation.
mutable struct ReplaceableMatrix
matrix::Matrix{Float64}
end
struct MyType
some_field::SomeType
matrix::ReplaceableMatrix
end
make MyType mutable with all fields immutable, except for matrix:
mutable struct MyType
const some_field::SomeType
matrix::ReplaceableMatrix
end
It would be possible to add a resize! method for Array, however, as some have pointed out, the desired semantics of such a method would not be obvious (what should happen to the existing elements).