I'm new in the area of Fortran programming and after testing a few programs I already got a feeling of how to write programs. Now I was trying me on a bit harder program and I run into a problem I couldn't solve by myself. I already googled about the problem but I couldn't find an adequate answer ...
So I thought maybe there are one or two Fortran programmers which can give me a hand in solving this problem.
The program is very simple it just multiplies two matrices with each other. I was trying to write a function which performs the task and returns the result matrix back to the invoker.
To make the program a bit more dynamic I used dynamic arrays where the user can specify the dimension the matrices should have.
The program looks like this:
program matrixMul
implicit none
integer, parameter :: ikind=selected_int_kind(18)
integer(kind=ikind), allocatable, dimension(:,:) :: m1, m2, result, mulMatrix
integer :: rows, cols, i, j
print *, 'Please enter the number of rows the matrix should have: '
read *, rows
print *, 'Please enter the number of columns the matrix should have: '
read *, cols
!allocate sufficient memory
allocate(m1(rows, cols), m2(cols, rows))
!fill matrixes with numbers entered by the user
call fillMatrix(m1, rows, cols)
call fillMatrix(m2, cols, rows)
result = mulMatrix(m1, m2, rows, cols)
!prints the result matrix to the screen
call printMatrix(result, rows, cols)
!deallocate memory
deallocate(m1, m2, mulMatrix, result)
end program matrixMul
Where the function which performs the actual multiplication looks like this:
function mulMatrix(m1, m2, r, c) result(mulMat)
implicit none
integer, parameter :: ikind=selected_int_kind(18)
integer(kind=ikind), dimension(r, c) :: m1
integer(kind=ikind), dimension(c, r) :: m2
integer(kind=ikind), allocatable, dimension(:,:) :: mulMat
integer r, c, i, j, k
allocate(mulMat(r,r))
!code which performs calculation is omitted
end function mulMatrix
My compiler reports the following error:
Error: Array index at (1) is an array of rank 2
For me it seems as the compiler would treat the variable mulMatrix as an usual array and therefore complains about it because I use it as it would have four dimensions which isn't the case. But how can I make the compiler think of this variable as a function call with four parameters instead of an array access?
Any help would be appreciate.
As IRO-bot already mentioned, use module. This will be a very good practice since you are learning a new language. With a module, you will just have to use the module instead of declaring individually the functions. It is also a good practice to not use keywords as variable names (example: result), use something else. I supposed that you understand well the trick of returning allocatable arrays as fonction return value, introduced in fortran 2003.
Your program can look like this, (the functions are contained in the program file instead of in a separate module, the result is the same)
program matrixMul
implicit none
integer, parameter :: ikind=selected_int_kind(18)
integer(kind=ikind), allocatable, dimension(:,:) :: m1, m2, m3
integer :: rows, cols, i, j
print *, 'Please enter the number of rows the matrix should have: '
read *, rows
print *, 'Please enter the number of columns the matrix should have: '
read *, cols
!allocate sufficient memory
allocate(m1(rows, cols), m2(cols, rows))
!fill matrixes with numbers entered by the user
call fillMatrix(m1, rows, cols)
call fillMatrix(m2, cols, rows)
m1 = 1
m2 = 1
m3 = mulMatrix(m1, m2, rows, cols)
!prints the result matrix to the screen
call printMatrix(m3, rows, cols)
!deallocate memory
deallocate(m1, m2, m3)
contains
function mulMatrix(m1, m2, r, c) result(mulMat)
implicit none
integer, parameter :: ikind=selected_int_kind(18)
integer(kind=ikind), dimension(r, c) :: m1
integer(kind=ikind), dimension(c, r) :: m2
integer(kind=ikind), allocatable, dimension(:,:) :: mulMat
integer r, c, i, j, k
allocate(mulMat(r,r))
!code which performs calculation is omitted
end function mulMatrix
end program matrixMul
Another think is: if you are performing the matrix multiplication as defined in linear algebra, the resulting matrix will be rows x rows, see the call to print the result.
A caviar will be to not add the size of the matrices as parameters (see M. S. B. comment) and use the intrincsic function LBOUND and UBOUND or SIZE to get them in the function.
Related
I am a bit confused on this subroutine. I have read the documentation but I am a bit confused what exactly the IPIV vector does and how exactly I set my leading dimension. I read that the leading dimension helps to find the starting point for the matrix elements in each successive column of the array. For example lets say we want to solve
Ax = B
where
integer, parameter :: sp = selected_real_kind(6,37)
real(kind=sp),dimension(:,:),intent(inout) :: A
real(kind=sp),dimension(:),intent(inout) :: B
integer, dimension(10) :: IPIV
where sp is for single precision which I have set in my main program
and the dimensions are
A(10,10)
B(10)
which are set in my main program and passed to this subroutine
Should I set my subroutine as
integer :: n,INFO
n = size(A,1)
IPIV = 0
call SGESV(n,n,A,2*n,IPIV,B,2*n,INFO)
or
call SGESV(n,n,A,n,IPIV,B,n,INFO)
and for IPIV I should just create a vector of size 10 and initialize it with zeros?
edit : I have used
call sgesv(n, n, A, n, ipiv, B, n, INFO)
as proposed as well but I get a segmentation error Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
I have printed the matrix sizes and they are correct which are the size of the matrix A is 100 and the size of the vector is 10
Edit2 : So in my main I have a loop which inside my loop it calculates a matrix of A (10,10) and a vector B(10) at each iteration. Then I call my subroutine to solve the system
call solver(A,B)
However I get the segmentation error which I do not understand since the dimensions are correct. (To check it I printed the size of the matrix and the vector and commented out the call to my subroutine and they are 100 and 10)
Perhaps I should make my matrices allocatable? But I do not see a problem with that since at each iteration I calculate the matrix and the vector though a series of calculations and overwrite them.
Basically I declare the matrix and the vector as follows
real(sp) , dimension (10) :: B
real(sp) , dimension (10,10) :: A
then inside my loop a series of calculations are performed to fill them with values
and then I call my subroutine
and then repeat with new values
You are using an old interface to lapack. Note my lower answer for the modern/generic routine.
Old interface
You would call it like
call sgesv(n, n, A, n, ipiv, B, n, info)
Reasoning:
leading dimensions are n and not 2n
ipiv is an output variable s.t. you dont need to initialize it with 0
Modern interface: LAPACK95
It is alot easier to just use the modern interfaces which provide generic calls as such
call gesv(A, B, ipiv=ipiv, info=info)
You dont need to specify the data types (e.g. no more sgesv) nor matrix dimensions.
Make sure that you need to use the appropriate module
use lapack95
Below is an example of calling gesv the generic Lapack95 equivalent (and much simpler) of sgesv and dgesv.
subroutine test_lapack95(n)
use BLAS95
use LAPACK95
use f95_precision
implicit none
integer, intent(in) :: n
real(float), allocatable :: A(:,:), LU(:,:)
real(float), allocatable :: b(:), x(:)
integer, allocatable :: ipiv(:)
allocate(A(n,n))
allocate(b(n))
allocate(ipiv(n))
! Fill values in A and b
call prepare_values(n, A, b)
LU = A
x = b
call gesv(LU,x,ipiv)
! Solve to A*x=b, for x
end subroutine
don't worry about the helper function prepare_values, it just fill in A and b.
This question already has answers here:
Fortran array rank for matmul intrinsic
(2 answers)
Vector multiplication using MATMUL in Fortran
(1 answer)
Closed 4 years ago.
After a few years of not using Fortran I'm having some troubles with matmul. Suppose I have two matrix: A_{N,K} and B_{J,K} . I want to create a subroutine that takes a row of A and a row of B, multiplies that and creates the scalar C. This is what I wrote:
subroutine test_matmul(A, B, N, K, J, row_a, row_b, C)
integer, intent(in) :: N, K, J, row_a, row_b
double precision, dimension(N,K), intent(in) :: A
double precision, dimension(J,K), intent(in) :: B
double precision, intent(out) :: C
C = matmul(A(row_a,:), B(row_b,:))
end subroutine test_matmul
Alas, when I try to compile this I get the following error:
C = matmul(A(row_a,:), B(row_b,:))
1
Error: 'matrix_b' argument of 'matmul' intrinsic at (1) must be of rank 2
What am I doing wrong?
The error is clear matmul multiplies two matrices and you are trying to pass two vectors. If you convert the vectors to matrices, matmul will through another error about matrices dimensions mismatch.
You should try using dot_product(vector_a, vector_b) instead of matmul.
I am given this code:
...
IMPLICIT REAL*8(a-h,o-z)
DIMENSION L1(L), L2(M), L3(N)
...
I want to use IMPLICIT NONE but I don't know how to declare variable type using DIMENSION and maintain one line declaration of L1 to L3.
Something like:
INTEGER, DIMENSION :: L1(L), L2(M), L3(N) !(this doesn't work)
The syntax for the dimension statement differs from that of specifying the dimension attribute in a declaration statement.
So, whereas
dimension i(4) ! Implicitly typed
gives i array nature of size 4,
integer, dimension(4) :: i
is the way to go.
Now to come to your question about declaring multiple arrays in one line:
integer i(4), j(5), k(6)
Finally, one can still use
integer, dimension(4) :: i, j(5), k(6), l
making i and l arrays of size 4 and j and k arrays of size 5 and 6.
I want to find the smallest eigenvalue from DSYEV and I'm not sure what I'm meant to put into the code for DSYEV.
Say my matrix A is 45x45 and I want to find its eigenvalues. So far I have:
subroutine eigenvalues()
implicit none
real(kind=8),allocatable,dimension(:,:)::A
real(kind=8),allocatable,dimension(:)::WORK, W
integer, allocatable, dimension(:)::t
integer::info,k,Z
t = shape(A)
k = t(1)
allocate(W(k))
print *, shape(M)
Z = 3*k-1
call dsyev('N','U',k,M,k,W,WORK,Z,info)
end subroutine eigenvalues
I'm not really sure what is meant by choosing to store the upper triangular matrix either. I don't know what LWORK means from the documentation still.
You want to calculate the eigenvalues of A but call your dsyev with M
You declare, but don't allocate A
You neither declare, define or allocate M
Where do your matrices come from? You may have to pass them to your subroutine if you calculate them somewhere else. Then you need to pass the dimensions also.
subroutine eigenvalues(A,k,k,eigvalues)
!calling list
integer, intent(in) :: k
double precision, intent(inout) :: A(k,k), eigvalues(k)
!local
double precision,allocatable :: work(:)
integer :: lwork,info
lwork = max(1,3*k-1)
allocate(work(lwork))
call dsyev('N','U',k,A,k,eigvalues,WORK,LWORK,info)
if(info .neq. 0) exit
Something like this (not complete example).
You will need to allocate your eigenvalue vector in the calling routine aswell, im sure you need the eigenvalues there...
WORK and LWORK don't really need to concern you. About Upper and Lower, look at your matrix A before and after your dsyev call...
I would like to calculate the Manhattan distance between 2 arrays in Fortran according to the formula:
d = Sum(|P(i)-R(i)|)
So I made a code in Fortran:
function DistM(v, u, dim)
integer dim
real(8) v(dim), u(dim), DistM
DistM=sum(abs(v-u))
end function DistM
I call this function by using this:
Coeff=DistM(tempvector1,tempvector2, dim)
But this doesn't seem to work (I don't get any return). I also tried by taking the power of 2 and then doing the sqrt of it, but it gets stuck at the sqrt then (if I run it in steps).
I tried another function and that one worked (see here after), but this one doesn't work :(:
function Roznica(v, u, dim)
integer dim
real(8) v(dim), u(dim), Rozn
Rozn=sum((v-u)**2)/dim
end function Roznica
Anyone an idea?
I would write a Manhattan distance function simply like this (with assumed-shape arrays you no longer have to use automatic arrays like you do, provided the function has an explicit interface):
pure function L1(v)
real, intent(in) :: v(:) ! <- shape assumed from actual argument
real :: L1
L1 = sum(abs(v))
end function
And then if you have two vectors you simply call the function as foo = L1(p - q).