Call FORTRAN subroutine with allocatables in R? - r

This is a follow-up question to my previous Fortran question.
I have a working Fortran program that has a subroutine that filters an array. This is the program:
program test
integer, parameter :: n = 3
integer, parameter :: m = 4
double precision, dimension(n,m) :: A
double precision, dimension(:,:), allocatable :: B
A(1,:) = [11, 22, 43, 55]
A(2,:) = [15, 56, 65, 63]
A(3,:) = [54, 56, 32, 78]
print*, 'A :'
print*, int(A)
CALL extractB(A, B)
print*, 'B'
print*, int(B)
contains
subroutine extractB(A, B)
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(:,:), allocatable :: B
integer :: nrowB, i, pos
nrowB = count( A(:,2)==56)
allocate( B(nrowB, size(A,2)-1 ) )
pos = 1
do i = 1, size(A,1)
if(A(i,2)==56)then
B(pos,1) = A(i,1)
B(pos,2:) = A(i,3:)
pos = pos+1
end if
end do
end subroutine extractB
end program
The program compiles, runs, and it does what it has to do very well.
I want to call the extractB subroutine with R. I have asked similar questions and found was able to make them work, but this one is somehow different and not working.
My fortran subrutine is in the mytest.f90 file and has this code:
subroutine extractB(A, B)
implicit none
double precision, dimension(:,:), intent(in) :: A
double precision, dimension(:,:), allocatable :: B
integer :: nrowB, i, pos
nrowB = count( A(:,2)==56)
allocate( B(nrowB, size(A,2)-1 ) )
pos = 1
do i = 1, size(A,1)
if(A(i,2)==56)then
B(pos,1) = A(i,1)
B(pos,2:) = A(i,3:)
pos = pos+1
end if
end do
end subroutine extractB
I compile it in R and load the library with these commands:
system("R CMD SHLIB ./Fortran/mytest.f90")
dyn.load("./Fortran/mytest.so")
Then, in R, i create and pass a data frame to the subroutine
A = data.frame(c(11,15,54), c(22,56,56), c(43,65,32), c(55,63,78))
X<-.Fortran("extractB", A = unlist(A), B = numeric(6))
After that R crashes
*** caught segfault ***
address (nil), cause 'unknown'
Traceback:
1: .Fortran("extractB", A = unlist(A), B = numeric(6))
Possible actions:
1: abort (with core dump, if enabled)
2: normal R exit
3: exit R without saving workspace
4: exit R saving workspace
Selection:
If I change the subroutine by removing setting the dimensions by hand:
subroutine extract(A, B)
implicit none
double precision, dimension(3,4), intent(in) :: A
double precision, dimension(2,3) :: B
integer :: i, pos
pos = 1
do i = 1, size(A,1)
if(A(i,2)==56)then
B(pos,1) = A(i,1)
B(pos,2:) = A(i,3:)
pos = pos+1
end if
end do
end subroutine extract
recompile the library, and reload it. I can run
X<-.Fortran("extract", A = unlist(A), B = numeric(6))
dim(X$A) <- dim(A)
dim(X$B) <- c(2,3)
and get what I want
> X
$A
[,1] [,2] [,3] [,4]
[1,] 11 22 43 55
[2,] 15 56 65 63
[3,] 54 56 32 78
$B
[,1] [,2] [,3]
[1,] 15 65 63
[2,] 54 32 78
Any way of fixing this?
Thanks a lot for the help!

Though, as Vladimir F pointed out, this will not work with allocatables like this, you can allocate memory, that can be used by another language in Fortran. But you should use the pointer attribute in that case. However, you will not be able to make use of assumed shape arrays either. Instead of inferring the size of A, you should pass it in explicitly.
If you are willing to change the interface for this, you can get your functionality with something along these lines, (see also M. S. B.s answer to a related question):
subroutine extractB(A_ptr, lenX, lenY, B_ptr, nrowB) bind(c,name=extractB)
use iso_c_binding
implicit none
type(c_ptr), value :: A_ptr
integer(kind=c_int), value :: lenX, lenY
type(c_ptr) :: B_ptr
integer(kind=c_int) :: nrowB
real(kind=c_double), pointer :: A(:,:)
real(kind=c_double), pointer :: B(:,:)
integer :: i, pos
c_f_pointer(A_ptr, A, [lenX, lenY])
nrowB = count( A(:,2)==56)
allocate( B(nrowB, size(A,2)-1 ) )
!...
B_ptr = c_loc(B)
end subroutine extractB

Related

Store all r combinations of a list

I need a general program in fortran to obtain all possible combinations of r elements in a list of n elements. I have found this code that prints all the combinations (r=3, n =5) but I need them to be stored in an array.
I tried to record them as rows near the write statement but it does not work. Turning the recursive subprogram into a recursive function is also not working.
program combinations
implicit none
integer, parameter :: m_max = 3
integer, parameter :: n_max = 5
integer, dimension (m_max) :: comb
character (*), parameter :: fmt = '(i0' // repeat (', 1x, i0', m_max - 1) // ')'
call gen (1)
contains
recursive subroutine gen (m)
implicit none
integer, intent (in) :: m
integer :: n
if (m > m_max) then
write (*, fmt) comb
else
do n = 1, n_max
if ((m == 1) .or. (n > comb (m - 1))) then
comb (m) = n
call gen (m + 1)
end if
end do
end if
end subroutine gen
end program combinations
Firstly, mixing global variables and recursive procedures is a good way to cause a lot of unnecessary confusion and debugging, so let's turn comb and n_max into procedure arguments, use size(comb) to give m_max, and for now replace fmt with *:
program combinations
implicit none
integer :: comb(3)
call gen(comb, 1, 5)
contains
recursive subroutine gen(comb, m, n_max)
integer, intent(inout) :: comb(:)
integer, intent(in) :: m
integer, intent(in) :: n_max
integer :: n
if (m > size(comb)) then
write (*, *) comb
else
do n = 1, n_max
if ((m == 1) .or. (n > comb(m - 1))) then
comb(m) = n
call gen(comb, m+1, n_max)
end if
end do
end if
end subroutine gen
end program combinations
The next thing to note is there's a subtle bug in your code. The line
if ((m == 1) .or. (n > comb (m - 1))) then
isn't guaranteed to work if m=1. Fortran does not guarantee short-circuiting of logical operators, so even if (m == 1) evaluates to .true., the (n > comb (m - 1)) could be evaluated, causing a segfault. Let's get around this by introducing a variable n_min, and calculating it correctly:
recursive subroutine gen(comb, m, n_max)
integer, intent(inout) :: comb(:)
integer, intent(in) :: m
integer, intent(in) :: n_max
integer :: n
integer :: n_min
if (m > size(comb)) then
write (*, *) comb
else
if (m == 1) then
n_min = 1
else
n_min = comb(m-1) + 1
endif
do n = n_min, n_max
comb(m) = n
call gen (comb, m+1, n_max)
end do
end if
end subroutine gen
Okay, now we can start thinking about returning the combinations from gen. To do this, let's change gen from a subroutine into a function, and have it return a 2-D array. We're going to need to append one 2-D array onto another, so let's write a function to do that now:
function append_combinations(input, new_combinations) result(output)
integer, intent(in) :: input(:,:)
integer, intent(in) :: new_combinations(:,:)
integer, allocatable :: output(:,:)
allocate(output(size(input,1), size(input,2)+size(new_combinations,2)))
output(:, :size(input,2)) = input
output(:, size(input,2)+1:) = new_combinations
end function
and now the whole program looks like
program combinations
implicit none
integer :: comb(3)
integer, allocatable :: combs(:,:)
integer :: i
combs = gen(comb, 1, 5)
write(*, *) ""
do i=1,size(combs,2)
write(*, *) combs(:,i)
enddo
contains
recursive function gen(comb, m, n_max) result(combs)
integer, intent(inout) :: comb(:)
integer, intent(in) :: m
integer, intent(in) :: n_max
integer, allocatable :: combs(:,:)
integer :: n
integer :: n_min
integer, allocatable :: new_combs(:,:)
if (m > size(comb)) then
write (*, *) comb
combs = reshape(comb, [size(comb),1])
else
if (m == 1) then
n_min = 1
else
n_min = comb(m-1) + 1
endif
allocate(combs(size(comb), 0))
do n = n_min, n_max
comb(m) = n
new_combs = gen(comb, m+1, n_max)
combs = append_combinations(combs, new_combs)
end do
end if
end function gen
function append_combinations(input, new_combinations) result(output)
integer, intent(in) :: input(:,:)
integer, intent(in) :: new_combinations(:,:)
integer, allocatable :: output(:,:)
allocate(output(size(input,1), size(input,2)+size(new_combinations,2)))
output(:, :size(input,2)) = input
output(:, size(input,2)+1:) = new_combinations
end function
end program combinations

Arguments of different types in a same Fortran subroutine

I want to define a subroutine in modern fortran (90 or newer) to print a matrix (with integer or real numbers) or vectors (with integer o numbers).
SUBROUTINE print_matrix_vector( A )
! WHAT TYPE CAN I WRITE TO A ???
END SUBROUTINE
Of course, I wanted to call a subrutine
CALL print_matrix_vector( A )
independent if A is a matrix of real or integer numbers, and if A is a vector of real or integer numbers.
Thanks in advance for your comments. I think this is something very usual, but I did not find nothing clear.
So Fortran uses interface statements to declare overloads to functions. I have done the exact same thing recently. The key is to create an interface, I call show which is used as an alias for all the separate functions needed.
interface show
procedure show_vector_i, show_vector_r, show_vector_d
procedure show_matrix_i, show_matrix_r, show_matrix_d
end interface
Here is the output first.
Display Matrices/Vectors
vector=
1
2
3
4
matrix=
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
A=
4.47723 3.36660 1.48809 -.752551
6.36660 7.19091 6.67333 5.54482
7.48809 9.67333 10.1187 9.77902
8.24745 11.5448 12.7790 13.0861
u=
6.36660
7.19091
6.67333
5.54482
v=
3.36660
7.19091
9.67333
11.5448
and the code
program FortranConsole1
use, intrinsic :: iso_fortran_env
implicit none
interface show
procedure show_matrix_i, show_matrix_r, show_matrix_d
procedure show_vector_r, show_vector_d
end interface
integer :: row(16), matrix(4,4), i
real(real64), allocatable, target :: A(:,:)
real(real64), pointer :: B(:,:)
real(real64), allocatable :: v(:), u(:)
row = [(i,i=1,16)]
matrix = reshape( row, [4, 4])
print *, "Display Matrices/Vectors"
print *, ""
print *, "vector="
call show(row(1:4))
print *, "matrix="
call show(matrix)
A = dble(matrix)
A = sqrt( matmul( transpose(A), A) ) - A
print *, "A="
call show(A)
v = A(:,2)
u = A(2,:)
print *, "u="
call show(u)
print *, "v="
call show(v)
contains
subroutine show_vector_i(v, w)
! Display the vector 'v' in a single column
! v : the array of real numbers
! w : the column width. default = 5
! s : sig. figures w-5 (calculated)
integer, intent(in) :: v(:)
integer, intent(in), optional :: w
integer :: i,n,wt
character(len=16) :: fmt
if(present(w)) then
wt = w
else
wt = 5
end if
n = size(v)
write( fmt, "(a,g0,a)") "(*(g",wt,".0))"
write( * , fmt ) ( v(i), new_line("A"), i=1,n )
end subroutine
subroutine show_vector_r(v, w)
! Display the vector 'v' in a single column
! v : the array of real numbers
! w : the column width. deafult = 12
! s : sig. figures w-5 (calculated)
real(real32), intent(in) :: v(:)
integer, intent(in), optional :: w
integer :: i,n,dg,wt
character(len=16) :: fmt
if(present(w)) then
wt = w
else
wt = 12
end if
dg = wt - 6
n = size(v)
write( fmt, "(a,g0,a,g0,a)") "(*(g",wt,".",dg,"))"
write( * , fmt ) ( v(i), new_line("A"), i=1,n )
end subroutine
subroutine show_vector_d(v, w)
! Display the vector 'v' in a single column
! v : the array of real numbers
! w : the column width. deafult = 12
! s : sig. figures w-5 (calculated)
real(real64), intent(in) :: v(:)
integer, intent(in), optional :: w
call show_vector_r(real(v),w)
end subroutine
subroutine show_matrix_i(A, w)
! Display the matrix 'A' in columns
! A : the array of integers
! w : the column width. Default = 5
integer, intent(in) :: A(:,:)
integer, intent(in), optional :: w
integer :: i,j,n,m, wt
character(len=16) :: fmt
if(present(w)) then
wt = w
else
wt = 5
end if
n = size(A,1)
m = size(A,2)
write( fmt, "(a,g0,a)") "(*(g",wt,".0))"
write( * , fmt ) ( (A(i,j),j=1,m), new_line("A"), i=1,n )
end subroutine
subroutine show_matrix_r(A, w)
! Display the matrix 'A' in columns
! A : the array of real numbers
! w : the column width. deafult = 12
! s : sig. figures w-5 (calculated)
real(real32), intent(in) :: A(:,:)
integer, intent(in), optional :: w
integer :: i,j,n,m,dg,wt
character(len=16) :: fmt
if(present(w)) then
wt = w
else
wt = 12
end if
dg = wt - 6
n = size(A,1)
m = size(A,2)
write( fmt, "(a,g0,a,g0,a)") "(*(g",wt,".",dg,"))"
write( * , fmt ) ( (A(i,j),j=1,m), new_line("A"), i=1,n )
end subroutine
subroutine show_matrix_d(A,w)
! Display the matrix 'A' in columns
! A : the array of dble numbers
! w : the column width. default = 12
! Converts 'A' into single precision and calls `show_matrix_r`
real(real64), intent(in) :: A(:,:)
integer, intent(in), optional :: w
call show_matrix_r(real(A),w)
end subroutine
end program FortranConsole1
In reality, the functions should be in a module file in order to be reused again and again in different programs.

get n dimensional Fortran array from subroutine output into R?

I have the following Fortran subroutine:
subroutine test(d, i, nMCd, DF, X)
integer, intent(in) :: d, i, nMCd
double precision, intent(in), dimension(i,nMCd) :: DF
double precision, intent(out), dimension(i) :: X
X = DF(:,d)+DF(:,d)
end subroutine test
I am able to compile it for R load it and run it. But instead of getting an array I'm getting a single number.
system("R CMD SHLIB ./Fortran/mytest.f90")
dyn.load("./Fortran/mytest.so")
input <- data.frame(A=c(11,12), B=c(21, 22))
.Fortran("test", d = as.integer(1), i = nrow(input), nMCd = ncol(input), DF = unlist(input), X = as.numeric(1))
What am I doing wrong?!
My output looks like
$d
[1] 1
$i
[1] 2
$nMCd
NULL
$DF
A1 A2 B1 B2
11 12 21 22
$X
[1] 22
The R version of this is:
input[,1]+input[,1]
I haven't figured out what this was supposed to do because I don't program in FORTRAN (And you didn't say what you expected in a language that I do read) but this is an experiment that delivers the sum of the items in the first columns of the input object, which might make some sense when I look at the code with the inputs. It seems possible that sending 1 for d to extract from DF(:,d)+ DF(:,d) might mean you wanted the sums of first columns. Notice that I just supplied an empty 4 element vector to X and made its Fortran dimensions the same as DF:
Source in file:
subroutine test(d, i, nMCd, DF, X)
integer, intent(in) :: d, i, nMCd
double precision, intent(in), dimension(i,nMCd) :: DF
double precision, intent(out), dimension(i,nMCd) :: X(i)
X = DF(:,d)+DF(:,d)
end subroutine test
R code:
input <- data.frame(A=c(11,12), B=c(21, 22))
.Fortran("test", d = as.integer(1), i = nrow(input), nMCd = ncol(input),
DF = unlist(input), X = numeric(4))
#--- result------
$d
[1] 1
$i
[1] 2
$nMCd
[1] 2
$DF
A1 A2 B1 B2
11 12 21 22
$X
[1] 22 24 0 0
Further experiment, still without any knowledge of Fortran, trying to add the items in the first row together:
X = DF(d,:)+DF(d,:)
Produced:
$X
[1] 22 42 0 0

Use Fortran subroutine in R? Undefined symbol

This is a follow up to my previous question. I wrapped my Fortran code in a module and now it compiles when I run:
R CMD SHLIB ./Fortran/Fpi.f90
This is my Fortran code:
Module Fpi
IMPLICIT NONE
contains
subroutine pi(avepi, DARTS, ROUNDS)
double precision, intent(out) :: avepi
integer, intent(in) :: DARTS, ROUNDS
integer :: MASTER, rank, i, n
integer, allocatable :: seed(:)
double precision :: pi_est, homepi, pirecv, pisum, dboard
! we set it to zero in the sequential run
rank = 0
! initialize the random number generator
! we make sure the seed is different for each task
call random_seed()
call random_seed(size = n)
allocate(seed(n))
seed = 12 + rank*11
call random_seed(put=seed(1:n))
deallocate(seed)
avepi = 0
do i = 0, ROUNDS-1
pi_est = dboard(DARTS)
! calculate the average value of pi over all iterations
avepi = ((avepi*i) + pi_est)/(i + 1)
end do
end subroutine pi
double precision function dboard(darts)
integer, intent(in) :: darts
double precision :: x_coord, y_coord
integer :: score, n
score = 0
do n = 1, darts
call random_number(x_coord)
call random_number(y_coord)
if ((x_coord**2 + y_coord**2) <= 1.0d0) then
score = score + 1
end if
end do
dboard = 4.0d0*score/darts
end function
end module Fpi
I'm trying to run this in R:
mypi <- function(DARTS, ROUNDS) {
dyn.load("./Fortran/Fpi.so")
retvals <- .Fortran("pi", DARTS = as.integer(DARTS) , ROUNDS = as.integer(ROUNDS), avepi = as.numeric(1))
return(retvals$avepi)
}
mypi(DARTS = 50000, ROUNDS = 10)
and I get this error:
Error in dyn.load("./Fortran/Fpi.so") :
unable to load shared object '/home/ignacio/local/projects/PI/./Fortran/Fpi.so':
/home/ignacio/local/projects/PI/./Fortran/Fpi.so: undefined symbol: dboard_
Your problem comes down to the declaration of dboard:
double precision :: pi_est, homepi, pirecv, pisum, dboard
Here you are saying that dboard is an external function, rather than a module procedure. This explains why there is a symbol dboard_ coming into play. You want to remove that:
double precision :: pi_est, homepi, pirecv, pisum
and instead rely, in pi on the module procedure-ness of dboard: pi already knows about it without this declaration.
Now, beyond that, because pi is in a module there is going to be some name mangling going on for that subroutine itself. I'd solve this problem by making pi itself a (C) interoperable procedure.
Module Fpi
IMPLICIT NONE
contains
subroutine pi(avepi, DARTS, ROUNDS) bind(C)
use, intrinsic :: iso_c_binding, only : c_double, c_int
real(c_double), intent(out) :: avepi
integer(c_int), intent(in) :: DARTS, ROUNDS
...
and then using .C rather than .Fortran.
You can keep pi and dboard in the module, and this latter needn't even be interoperable.
Try to fix the name of the fortran call inside the R function. You typed "pi" where it should be "Fpi". Also, why not bring the function to your path instead of passing a long path inside the function?
mypi <- function(DARTS, ROUNDS) {
dyn.load("./Fortran/Fpi.so")
retvals <- .Fortran("Fpi", DARTS = as.integer(DARTS) , ROUNDS = as.integer(ROUNDS), answer = as.numeric(1))
return(retvals$answer)
}
mypi(DARTS = 50000, ROUNDS = 10)
Aparently I cannot wrap the subroutines in a module. This fortran code is working:
subroutine dboard(darts, dartsscore)
implicit none
integer, intent(in) :: darts
double precision, intent(out) :: dartsscore
double precision :: x_coord, y_coord
integer :: score, n
score = 0
do n = 1, darts
call random_number(x_coord)
call random_number(y_coord)
if ((x_coord**2 + y_coord**2) <= 1.0d0) then
score = score + 1
end if
end do
dartsscore = 4.0d0*score/darts
end subroutine dboard
subroutine pi(avepi, DARTS, ROUNDS)
implicit none
double precision, intent(out) :: avepi
integer, intent(in) :: DARTS, ROUNDS
integer :: MASTER, rank, i, n
integer, allocatable :: seed(:)
double precision :: pi_est, homepi, pirecv, pisum
interface
subroutine dboard(darts, dartsscore)
implicit none
integer, intent(in) :: darts
double precision, intent(out) :: dartsscore
end subroutine dboard
end interface
! we set it to zero in the sequential run
rank = 0
! initialize the random number generator
! we make sure the seed is different for each task
call random_seed()
call random_seed(size = n)
allocate(seed(n))
seed = 12 + rank*11
call random_seed(put=seed(1:n))
deallocate(seed)
avepi = 0
do i = 0, ROUNDS-1
call dboard(darts, pi_est)
! calculate the average value of pi over all iterations
avepi = ((avepi*i) + pi_est)/(i + 1)
end do
end subroutine pi
and this is the R code:
system("R CMD SHLIB ./Fortran/Fpi.f90")
mypi <- function(DARTS, ROUNDS) {
dyn.load("./Fortran/Fpi.so")
retvals <- .Fortran("pi", avepi = as.numeric(1), DARTS = as.integer(DARTS), ROUNDS = as.integer(ROUNDS))
return(retvals$avepi)
}
mypi(DARTS = 50000, ROUNDS = 10)

Fortran Matrix Multiplication isn't giving the right answers

I've got a matrix calculator program, but I'm getting the wrong answer for my dot product multiplier.
Here's my multiply subroutine:
subroutine multiply(m1,m2,res,row1,row2,col1,col2)
integer, intent(in) :: row1,row2,col1,col2
real, intent(in), dimension(row1,col1) :: m1
real, intent(in), dimension(row2,col2) :: m2
real, intent(out), dimension(row1,col2) :: res
integer :: i,j,k
do i = 1, col2
do j = 1, col1
res(j, i) = 0
enddo
do j = 1, col1
do k = 1, row1
res(k, i) = res(k, i) + m1(k, j)*m2(j, i)
enddo
enddo
enddo
And here's my output, just in case that's the problem.
subroutine output(r,c,matrix,name)
integer, intent(in) :: r
integer, intent(in):: c
character(len=10) :: name
real, intent(out), dimension(3,3) :: matrix
integer i,j
print *,name
do i = 1, r
write(*,"(100F6.1)") ( matrix(i,j), j=1,c )
enddo
end subroutine
If it helps, it works excellently for a 3x3 matrix, but not for two rectangular matrix. Here's what happens when I do a 2x3 * 3x2 matrix. At this point, i'm desperate for help. Anything you could suggest would be great. Thanks!
EDIT: Here's all of my code in its current state with your suggestions.
PROGRAM G6P5
integer :: r1,r2,c1,c2,i,j,k,s
real :: input
real, dimension (3,3) :: mat1, mat2, rmat
write (*,*) 'Please make a selection:'
write (*,*) 'Enter 1 to add matrices'
write (*,*) 'Enter 2 to subtract matrices'
write (*,*) 'Enter 3 to multiply matrices'
write (*,*) 'Enter 4 to transpose a matrix'
write (*,*) 'Enter 5 to quit'
read *, s
select case (s)
case (1)
print *, 'Enter # of rows & columns (1-10) (ex. 3 3 = 3x3)'
read *, r1,c1
print *, 'Matrix 1:'
call fillmatrix(r1,c1,mat1)
r2 = r1
c2 = c1
print *, 'Matrix 2:'
call fillmatrix(r2,c2,mat2)
call output(r1,c1,mat1,'Matrix 1: ')
call output(r2,c2,mat2,'Matrix 2: ')
rmat = mat1+mat2
call output(r1,c1,rmat,'Sum: ')
case (2)
print *, 'Enter # of rows & columns (1-10) (ex. 3 3 = 3x3)'
read *, r1,c1
print *, 'Matrix 1:'
call fillmatrix(r1,c1,mat1)
r2 = r1
c2 = c1
print *, 'Matrix 2:'
call fillmatrix(r2,c2,mat2)
rmat = mat1-mat2
call output(r1,c1,mat1,'Matrix 1: ')
call output(r2,c2,mat2,'Matrix 2: ')
call output(r1,c1,rmat,'Sum: ')
case (3)
print *, 'Enter # of rows & columns for matrix 1'
print *, '(1 through 10, ex: 3 3 = 3x3)'
read *, r1,c1
print *, 'Matrix 1:'
call fillmatrix(r1,c1,mat1)
print *, 'Enter # of rows & columns for matrix 2'
print *, '(1 through 10, ex: 3 3 = 3x3)'
read *, r2,c2
print *, 'Matrix 2:'
call fillmatrix(r2,c2,mat2)
if (c1.eq.r2) then
call multiply(mat1,mat2,rmat,r1,r2,c1,c2)
call output(r1,c1,mat1,'Matrix 1: ')
call output(r2,c2,mat2,'Matrix 2: ')
call output(r1,c2,rmat,'Product: ')
end if
case (4)
print *, 'Enter # of rows & columns for matrix 1'
print *, '(1 through 10, ex: 3 3 = 3x3)'
read *, r1,c1
print *, 'Matrix 1:'
call fillmatrix(r1,c1,mat1)
call transpose(mat1,rmat,r1,c1)
call output(r1,c1,rmat,'Transpose:')
case (5)
print *,'5'
case default
print *,'default'
end select
! call fillmatrix(rows,columns,mat1)
! write (*,*) matrix1
END PROGRAM
subroutine fillmatrix(r,c,matrix)
integer, intent(in) :: r
integer, intent(in):: c
real, intent(out), dimension(3,3) :: matrix
integer i,j
do i=1,r
do j = 1,c
write (*,'(A,I2,A,I2,A)') 'Enter value (',i,',',j,').'
read*, matrix(i,j)
enddo
enddo
end subroutine
subroutine multiply(m1,m2,res,row1,row2,col1,col2)
integer, intent(in) :: row1,row2,col1,col2
real, intent(in), dimension(row1,col1) :: m1
real, intent(in), dimension(row2,col2) :: m2
real, intent(out), dimension(row1,col2) :: res
integer :: i,j,k
res = 0
do i = 1, row1
do j = 1, col2
do k = 1, col1 ! col1 must equal row2
res(i, j) = res(i, j) + m1(i, k)*m2(k, j)
enddo ! k
enddo ! j
enddo ! i
end subroutine
subroutine transpose(m1,res,row,col)
integer, intent(in) :: row,col
real, intent(in), dimension(row,col) :: m1
real, intent(out), dimension(row,col) :: res
integer :: i,j,k
do i = 1,col
do j = 1,row
res(i,j) = m1(j,i)
enddo
enddo
end subroutine
subroutine output(r,c,matrix,name)
integer, intent(in) :: r
integer, intent(in):: c
character(len=10) :: name
real, intent(in), dimension(r,c) :: matrix
integer i,j
print *,name
do i = 1, r
write(*,"(100F6.1)") ( matrix(i,j), j=1,c )
enddo
end subroutine
You seem to have the indexing a little confused. Try this.
res = 0
do i = 1, col2
do j = 1, row1
do k = 1, col1
res(j, i) = res(j, i) + m1(j, k)*m2(k, i)
enddo
enddo
enddo
I also notice that in your output routine you have the line
real, intent(out), dimension(3,3) :: matrix
If you are sending matrix into this routine, it should be intent(in). Also, if you are printing a 2x2 matrix, then the dimension(3,3) is also incorrect. You should change this line to
real, intent(in) :: matrix(r,c)
One last thing you might consider. Your matrix is always 3x3, but you are not always using all the elements. That is why you pass the number of rows and columns to the subroutines. The issue here is that the actual size of the matrix needs to match these numbers. To do this, you need to use the slice notation.
Instead of
call sub(2,2,mat)
use
call sub(2,2,mat(1:2,1:2))
This is because the first method is essentially equivalent to
call sub(2,2,mat(1:3,1:3))
Which will result in a mismatch between what you are passing to the subroutine and what the subroutine expects. This can cause funny things to happen, as you saw.

Resources