Store all r combinations of a list - recursion

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

Related

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.

Writing R package that call Fortran library

I'm trying to write an R package that calls a Fortran subroutine. I'm using Rstudio package template that creates a bunch of files and directories automatically.
In ./R/ I have a single file, Fpi.R
Fpi <- function(DARTS, ROUNDS) {
if (!is.loaded('Fpi')) {
dyn.load("./src/Fpi.so")
}
retvals <- .Fortran("pi", avepi = as.numeric(1), DARTS = as.integer(DARTS), ROUNDS = as.integer(ROUNDS))
return(retvals$avepi)
}
In ./src/ I have Fpi.f90
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
I also have the generic DESCRIPTION and NAMESPACE files that rstudio generates.
I can build and load the library but when I try to use it I get this error:
> library(MyPi)
> Fpi(DARTS = 100, ROUNDS = 100)
Error in .Fortran("pi", avepi = as.numeric(1), DARTS = as.integer(DARTS), :
"pi" not resolved from current namespace (MyPi)
How can I fix this? Thanks!
I just had to add one line to my NAMESPACE
useDynLib(Fpi)

using a Fortran module in R?

I am able to use a fortran subroutine in R as long as I don't have it inside a module.For example:
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
Can be compiled for R with:
R CMD SHLIB ./Fortran/Fpi.f90
and I can run it in R with:
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)
I would like to write my fortran subroutines inside a module. I thought this was not possible, but #roygvib and #francescalus mentioned it is in one of my previous questions
How do you do the "attaching bind("c",name=...)" thing that #roygvib mentioned in that post?
Thanks!
I made the changes suggested in the comments:
Module Fortranpi
IMPLICIT NONE
contains
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) bind(C, name="pi_")
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
end module Fortranpi
When I try to build the package with Rstudio I get these errors:
==> R CMD INSTALL --no-multiarch --with-keep.source MyPi
* installing to library ‘/home/ignacio/R/x86_64-pc-linux-gnu-library/3.2’
make: Nothing to be done for 'all'.
* installing *source* package ‘MyPi’ ...
** libs
installing to /home/ignacio/R/x86_64-pc-linux-gnu-library/3.2/MyPi/libs
** R
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
Error in library.dynam(lib, package, package.lib) :
shared object ‘Fortranpi.so’ not found
Error: loading failed
Execution halted
ERROR: loading failed
* removing ‘/home/ignacio/R/x86_64-pc-linux-gnu-library/3.2/MyPi’
* restoring previous ‘/home/ignacio/R/x86_64-pc-linux-gnu-library/3.2/MyPi’
Exited with status 1.
I believe the problem could be the NAMESPACE
useDynLib(Fpi)
exportPattern("^[[:alpha:]]+")
Thanks to #roygvib and #francescalus this is my working module:
Module Fortranpi
IMPLICIT NONE
contains
subroutine dboard(darts, dartsscore)
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) bind(C, name="pi_")
use, intrinsic :: iso_c_binding, only : c_double, c_int
real(c_double), intent(out) :: avepi
integer(c_int), intent(in) :: DARTS, ROUNDS
integer :: MASTER, rank, i, n
integer, allocatable :: seed(:)
double precision :: pi_est, homepi, pirecv, pisum
! 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
end module Fortranpi

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