Transposition of data between Fortran and C can lead to a lot of unneccessary confusion.
The FTorch library looks after this for you with the
torch_tensor_from_array()
function which
allows you to index a tensor in Torch in exactly the same way as you would in Fortran.
If you wish to do something different to this then there are more complex functions available and we describe here how and when to use them.
Astute users will note that Fortran is a column-major language whilst C, C++, and Python are row-major.
This means that the matrix/tensor in Fortran
will appear in
contiguous memory
on the computer as
with the order of elements decided by moving down the columns before progressing in the
row dimension.
In contrast, the same matrix/tensor defined in a row-major language will appear in
contiguous memory as
reading along each row before progressing down the column dimension.
This matters for FTorch because a key feature is no-copy memory transfer between Fortran and Torch. To do this the Fortran data that will be used in Torch is stored in memory and a pointer to the first element, provided to Torch.
Now, if Torch were to take this block of memory and interpret it as as a 2x2 matrix it would be read in as which is the transpose of the matrix we had in Fortran; likely not what we were expecting!
This means we need to be careful when passing data to make sure that what we read in to our Torch net is correct as we expect.
There are a few approaches we can take to address this.
The first two of these are listed for conceptual purposes, whilst in practice we
advise handling this using the torch_tensor_from_array
function described in
3) below.
As seen from the above example, writing out from Fortran and reading directly in to Torch results in us recieving the transpose.
Therefore we could transpose out Fortran data immediately before passing it to Torch. As a result we will read in to Torch indexed the same as in Fortran pre-transposition.
For arrays of dimension 2 this can be done using the intrinsic
transpose()
function.
For larger arrays we are required to use the
'reshape()' intrinsic to swap
the order of the indices.
For example, if we had a 3x4x5 matrix we would need to call
A_to_torch = reshape(A, shape=[5, 4, 3], order=[3, 2, 1])
which could then be read by Torch as a 3x4x5 tensor.
We would, of course, need to remember to transpose/reshape any output of the model as required.
However, the transposition process involves creating a copy of the Fortran data. For large matrices/tensors this can become expensive. It would be better if we can pass data without having to transpose beforehand.
Alternatively we could design our net to use as its input tensor meaning we can simply write from Fortran and read to Torch.
However, this requires foresight and may not be intuitive - we would like to be indexing data in the same way in both Fortran and Torch. Not doing so could leave us open to introducing bugs.
layout
argument in torch_tensor_from_array
By far the easiest way to deal with the issue is not to worry about it at all!
As described at the top of this page, by using the
torch_tensor_from_array function
we can make use of the layout
argument.
This allows us to take data from Fortran and send it to Torch to be indexed in exactly
the same way by using strided access based on the shape of the array.
It takes the form of an array specifying which order to read the indices in.
i.e. [1, 2]
will read i
then j
.
By passing layout = [1, 2]
the data will be read into the correct indices by
Torch.
This is achieved by wrapping the torch_tensor_from_blob
function to automatically
generate strides assuming that a straightforward conversion between
row- and column-major is what should happen.
i.e. if the Fortran array A
is passed as torch_tensor_from_array(A, [1, 2], torch_device)
the resulting Tensor will be read by Torch as
Note: If, for some reason, we did want a different, transposed layout in Torch we could use
torch_tensor_from_array(A, [2, 1], torch_device)
to get:
torch_tensor_from_blob
For more advanced options for manipulating and controlling data access when passing between Fortran and Torch see the more powerful but more complex torch_tensor_from_blob function