How to initialize two-dimensional arrays in Fortran - arrays

In C you can easily initialize an array using the curly braces syntax, if I remember correctly:
int* a = new int[] { 1, 2, 3, 4 };
How can you do the same in Fortran for two-dimensional arrays when you wish to initialize a matrix with specific test values for mathematical purposes? (Without having to doubly index every element on separate statements)
The array is either defined by
real, dimension(3, 3) :: a
or
real, dimension(:), allocatable :: a

You can do that using reshape and shape intrinsics. Something like:
INTEGER, DIMENSION(3, 3) :: array
array = reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array))
But remember the column-major order. The array will be
1 4 7
2 5 8
3 6 9
after reshaping.
So to get:
1 2 3
4 5 6
7 8 9
you also need transpose intrinsic:
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array)))
For more general example (allocatable 2D array with different dimensions), one needs size intrinsic:
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(:, :), ALLOCATABLE :: array
ALLOCATE (array(2, 3))
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6 /), &
(/ size(array, 2), size(array, 1) /)))
DEALLOCATE (array)
END PROGRAM main

For multidimensional (rank>1) arrays, the Fortran way for initialization differs from the C solution because in C multidimensional arrays are just arrays of arrays of etc.
In Fortran, each rank corresponds to a different attribute of the modified data type. But there is only one array constructor, for rank-1 arrays. From these two reasons, initialization through array constructor requires the RESHAPE intrisic function.
In addition to what has already been answered, there is a more direct way of entering the value of a matrix by row instead as by column: reshape has an optional argument ORDER which can be used to modify the order of filling the element of the multidimensional array with the entries of the array constructor.
For instance, in the case of the example in the first answer, one could write:
INTEGER, DIMENSION(3, 3) :: array=reshape( (/ 1, 2, 3, &
4, 5, 6, &
7, 8, 9 /), &
shape(array), order=(/2,1/) )
obtaining the filling of the matrix array exactly in the order shown by the lines of code.
The array (/2, 1/) forces the column index (2) to have precedence on the row index (1), giving the desired effect.

Array initialization can be done in the array declaration statement itself, as shown below:
program test
real:: x(3) = (/1,2,3/)
real:: y(3,3) = reshape((/1,2,3,4,5,6,7,8,9/), (/3,3/))
integer:: i(3,2,2) = reshape((/1,2,3,4,5,6,7,8,9,10,11,12/), (/3,2,2/))
end program test
It surprises me that
real:: y(3,3) = (/(/1,2,3/),(/4,5,6/),(/7,8,9/)/)
is not accepted by the compiler (tried g95, gfortran). It turns out that the shape of
(/(/1,2,3/),(/4,5,6/),(/7,8,9/)/) is 9 and not 3 3!

Related

Is there a way to assign values to a multidimensional array in Fortran without using nested loops? [duplicate]

In C you can easily initialize an array using the curly braces syntax, if I remember correctly:
int* a = new int[] { 1, 2, 3, 4 };
How can you do the same in Fortran for two-dimensional arrays when you wish to initialize a matrix with specific test values for mathematical purposes? (Without having to doubly index every element on separate statements)
The array is either defined by
real, dimension(3, 3) :: a
or
real, dimension(:), allocatable :: a
You can do that using reshape and shape intrinsics. Something like:
INTEGER, DIMENSION(3, 3) :: array
array = reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array))
But remember the column-major order. The array will be
1 4 7
2 5 8
3 6 9
after reshaping.
So to get:
1 2 3
4 5 6
7 8 9
you also need transpose intrinsic:
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6, 7, 8, 9 /), shape(array)))
For more general example (allocatable 2D array with different dimensions), one needs size intrinsic:
PROGRAM main
IMPLICIT NONE
INTEGER, DIMENSION(:, :), ALLOCATABLE :: array
ALLOCATE (array(2, 3))
array = transpose(reshape((/ 1, 2, 3, 4, 5, 6 /), &
(/ size(array, 2), size(array, 1) /)))
DEALLOCATE (array)
END PROGRAM main
For multidimensional (rank>1) arrays, the Fortran way for initialization differs from the C solution because in C multidimensional arrays are just arrays of arrays of etc.
In Fortran, each rank corresponds to a different attribute of the modified data type. But there is only one array constructor, for rank-1 arrays. From these two reasons, initialization through array constructor requires the RESHAPE intrisic function.
In addition to what has already been answered, there is a more direct way of entering the value of a matrix by row instead as by column: reshape has an optional argument ORDER which can be used to modify the order of filling the element of the multidimensional array with the entries of the array constructor.
For instance, in the case of the example in the first answer, one could write:
INTEGER, DIMENSION(3, 3) :: array=reshape( (/ 1, 2, 3, &
4, 5, 6, &
7, 8, 9 /), &
shape(array), order=(/2,1/) )
obtaining the filling of the matrix array exactly in the order shown by the lines of code.
The array (/2, 1/) forces the column index (2) to have precedence on the row index (1), giving the desired effect.
Array initialization can be done in the array declaration statement itself, as shown below:
program test
real:: x(3) = (/1,2,3/)
real:: y(3,3) = reshape((/1,2,3,4,5,6,7,8,9/), (/3,3/))
integer:: i(3,2,2) = reshape((/1,2,3,4,5,6,7,8,9,10,11,12/), (/3,2,2/))
end program test
It surprises me that
real:: y(3,3) = (/(/1,2,3/),(/4,5,6/),(/7,8,9/)/)
is not accepted by the compiler (tried g95, gfortran). It turns out that the shape of
(/(/1,2,3/),(/4,5,6/),(/7,8,9/)/) is 9 and not 3 3!

Separating a matrix into sub-matrices in Fortran

Suppose I have a 2-D array such that the first column is composed of only two integers 1 and 2:
1 5 1 7 0.5
2 4 5 6 0.1
1 9 3 4 0.6
2 8 7 2 0.2
I want to separate two matrices out of this, such that the first column of each contains the same integer (so the first column of first matrix contains only integer 1, same goes for 2 in the second matrix).
So it would become:
1 5 1 7 0.5
1 9 3 4 0.6
and
2 4 5 6 0.1
2 8 7 2 0.2
I don't know exactly how to start. I was thinking of using the count at the beginning (well, because I have a way larger matrix with 10 different integers in the first column), then according to the counted number of each integer I construct the dimension of each [sub]matrix. After that, the only thing I could think of is the count(mask), and if the value is true it's then added to the matrix by if statement.
You can't have mixed types (integer and real) in the same array in Fortran, so I will suppose all data are real in the 2-dim array:
program split
implicit none
real, allocatable :: a(:, :), b(:, :)
integer :: i, ids = 10
integer, allocatable :: id(:), seq(:)
a = reshape([real :: 1, 5, 1, 7, 0.5, &
& 2, 4, 5, 6, 0.1, &
& 1, 9, 3, 4, 0.6, &
& 2, 8, 7, 2, 0.2], [5, 4])
seq = [(i, i = 1, size(a, 2))]
do i = 1, ids
print*, "i = ", i
! here we are creating a vector with all the line indices that start with i
! e.g. for i = 1 we get id = [1, 3], for i = 2 we get [2, 4], for i = 3 we get [], ...
id = pack(seq, a(1,:) == i)
! here we use a Fortran feature named vector-subscript
b = a(:, id)
print*, b
end do
end
If you want the first column(or any column) to be integer, you can declare it as a separated array, and use the same vector subscripts to gather the desired lines.

Fast way of summing up elements in one array by checking condition in second array [duplicate]

This question already has answers here:
Numpy sum elements in array based on its value
(2 answers)
Closed 5 years ago.
I have following three arrays
a = array([4, 4, 3, 5, 5, 4, 6, 5, 6])
b = array([0, 1, 2, 2, 2, 3, 3, 4, 4])
c = array([0,0,0,0,0])
I want to sum all elements in a for all elements in b. For example i want following array.
c = array([4,4,3+5+5,4+6,5+6])
I can do this exercise by running a for loop as follows
loop_array = scipy.unique(b)
for i in loop_array:
c[i]=sp.sum(a[b==i])
Since my original a and b arrays are very big, in the order of million, I cannot use for loop. Is there any faster way to solve this problem. Any array operation will be preferable if possible.
This should be faster, though it requires more memory for auxiliary arrays
slices1 = np.insert(np.nonzero(np.diff(b))[0]+1, 0, 0)
slices2 = np.insert(np.nonzero(np.diff(b))[0]+1, len(slices1)-1, len(b))
c = [np.sum(a[j[0]:j[1]]) for j in zip(slices1, slices2)]

Vectorizing Matlab replace array values from start to end

I have an array in which I want to replace values at a known set of indices with the value immediately preceding it. As an example, my array might be
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
and the indices of values to be replaced by previous values might be
y = [2, 3, 8];
I want this replacement to occur from left to right, or else start to finish. That is, the value at index 2 should be replaced by the value at index 1, before the value at index 3 is replaced by the value at index 2. The result using the arrays above should be
[1, 1, 1, 4, 5, 6, 7, 7, 9, 0]
However, if I use the obvious method to achieve this in Matlab, my result is
>> x(y) = x(y-1)
x =
1 1 2 4 5 6 7 7 9 0
Hopefully you can see that this operation was performed right to left and the value at index 3 was replaced by the value at index 2, then 2 was replaced by 1.
My question is this: Is there some way of achieving my desired result in a simple way, without brute force looping over the arrays or doing something time consuming like reversing the arrays around?
Well, practically this is a loop but the order is number of consecutive index elements
while ~isequal(x(y),x(y-1))
x(y)=x(y-1)
end
Using nancumsum you can achieve a fully vectorized version. Nevertheless, for most cases the solution karakfa provided is probably one to prefer. Only for extreme cases with long sequences in y this code is faster.
c1=[0,diff(y)==1];
c1(c1==0)=nan;
shift=nancumsum(c1,2,4);
y(~isnan(shift))=y(~isnan(shift))-shift(~isnan(shift));
x(y)=x(y-1)

Loop in Fortran from a list

I use Fortran and I was wondering if it's possible to make something like that
do i = array
write (*,*) i
end do
where array is a list of integer numbers not necessarily ordered.
I would introduce a second index to iterate over the elements of an array:
program test
implicit none
integer, dimension(6) :: A
integer, dimension(10) :: B
integer :: i, j
A = (/ 1, 3, 4, 5, 8, 9 /)
B = (/ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 /)
do j = 1, size(A)
i = A(j)
write(*,*) i, B(i)
end do
end program test
Do you mean that you want to write some of the elements of an array called other_array but not all of them, and that i should take, essentially, arbitrary values in turn ? In other words you want to print not
do i = 1, size(other_array,1)
write(*,*) other_array(i)
end do
but something like
array = [1,3,4,2,3,7,8,8,12]
write(*,*) another_array(array)
which will write the elements of another_array specified in array ? This is called array subscripting. I haven't tested this and I'm heading out now so won't.

Resources