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.
Related
I have an N-dimensional dataset (say real numbers) which is stored as a 1D array with an additional dimension array which specifies the original dimensions.
In addition, the functions to deduce the 1-D index from an N-D index and vice-versa are given.
I'm trying to figure out how do I make a do-loop (or equivalent) for the generic N-dimensional index (which will be transformed to 1D index of course) from some set of limiting lower indices to a set of upper indices.
So I need an "N-dimensional" loop which does not go over all of the values - only a portion of the array, therefore doing the linear index of an equivalent 1D array is not relevant (at least without modifications).
This is a schematic of my problem:
subroutine Test(Array,Dims,MinIndex,MaxIndex)
implicit none
real , dimension(1:), intent(inout) :: Array
integer, dimension(1:), intent(in) :: Dims,MinIndex,MaxIndex
integer, dimension(size(Dims)) :: CurrInd
integer :: 1Dindex
! size(Dims) can be 1, 2, 3 ,..., N
! size(MinIndex)==size(MaxIndex)==size(Dims)
! size(Array)==Product(Dims)
! 1Dindex=Get1dInd(NDindex,Dims)
! NDindex=GetNdInd(1Dindex,Dims)
! How do I actually preform this?
do CurrInd=MinIndex,MaxIndex
1Dindex=Get1dInd(CurrInd,Dims)
<Some operation>
enddo
end subroutine
I figured it is possible to loop over the Dims array and use an inner loop but I don't manage to write the procedure down properly.
Another option that didn't work for me (maybe because I use it incorrectly?) is FORALL, as that requires each index to be specified separately.
If you knew the dimensions of your arrays at compilation time you could do a series of nested DO loops, each of them running between pairs of components of MinIndex and MaxIndex. Since you don't know the dimensions, that's not possible.
The easiest strategy I can think of is to loop with a single DO loop over all the 1D indices. For each of them, compute the N-dimensional index, and check if it is within the bounds provided by MinIndex and MaxIndex: if it is, continue doing what you need; if it is not, discard that 1D index and go to the next one. If the indices are sequential you may be able to do something smarter that skips blocks of indices that you know you will not be interested in.
do OneDindex = 1, size(Array)
CurrInd = GetNDInd(OneDindex, Dims)
if ((any(CurrInd<MinIndex)) .or. (any(CurrInd>MaxIndex))) cycle
! <Some operation>
end do
Note that, as far as the index manipulation is concerned, this strategy is compatible with parallelising the loop.
Also note that Fortran variables must start with a letter: 1Dindex is not a valid Fortran variable name.
So this is the actual procedure I ended up with.
I have verified it and hope it will be useful to you as it was for me.
(I know this is not well commented, but the question has all the details and my time is extremely short at the moment)
Also, thanks ripero for helping out!
I decided not to use the CYCLE approach as I assume the code will work more efficiently when only the actual amount of loops are performed.
!-----------------------------------------------------------
subroutine Test(Array,Rank,Dims,InitInd,FinInd)
implicit none
real, dimension(1:), intent(inout) :: Array
integer, intent(in) :: Rank
integer, dimension(1:), intent(in) :: Dims
integer, dimension(1:), intent(in) :: InitInd,FinInd
!-----------------------------------------------------------
integer :: nOuter
integer :: i,j, OneDInd
integer, dimension(Rank) :: Curr
!-----------------------------------------------------------
! Check how many repetition for the outer-loop
Curr=FinInd-InitInd
nOuter=1
do i=2,Rank
nOuter=nOuter*(Curr(i)+1)
enddo
!-----------------------------------------------------------
! Actual looping:
Curr=InitInd
do j=1,nOuter
! Update minor indices (>1):
do i=1,Rank
if (Curr(i).GT.FinInd(i)) then
! Update next index:
Curr(i)=InitInd(i)
Curr(i+1)=Curr(i+1)+1
endif
enddo
! Loop over major index:
do i=InitInd(1),FinInd(1)
!OneDInd=Get1dInd(Curr,Dims)
!<operation>
! Advance major index:
Curr(1)=Curr(1)+1
enddo
enddo
end subroutine Test
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...
Let A and B be matrices of size 1 times n and n times 1, respectively.
Then the multiplication of A with B is a 1 times 1 matrix.
Which is the better way to assign the value of MATMUL(A,B) to a real number x?
I would like to write:
x=MATMUL(A,B) ! <<--- but this is wrong.
The above expression is wrong because I'm trying to assign a 1 times 1 matrix to a real number.
My solution is to define a 1 times 1 matrix C and with this:
C=MATMUL(A,B)
x=C(1,1) ! <--- this solution is ok, but is too long
But, there exists a better way to assign MATMUL(A,B) to the real number x?
The entire code with my question is as follow:
PROGRAM testing
!
IMPLICIT NONE
REAL :: A(1,2),B(2,1),C(1,1),x
!
A(1,1)=1.0; A(1,2)=3.5
B(1,1)=2.0; B(2,1)=5.0
C=MATMUL(A,B) ! it is ok
x=MATMUL(A,B) ! it is wrong
x=C(1,1) ! it is ok <--- exists a better way ??
!
END PROGRAM testing
You have noticed that it is not possible to do intrinsic assignment of an array to a scalar (and C is a rank-2 array of size 1). x=C(1,1) is the correct way to do such assignment from the single element of C to the scalar x.
There are other ways to abstract that correct assignment statement, but probably little value in doing so.
In your specific case, however, there is alternative. Rather than matmul, consider dot_product.
x = DOT_PRODUCT(A(1,:), B(:,1)) ! Scalar result, intrinsic assignment allowed.
per my comment, you can write a very simple function to extract the first element of an array:
real function first(matrix) !return the (1,1,1,..) element of an array
real, intent(in) :: matrix(*)
first=matrix(1)
end function
simply use as:
real :: a(1,2),b(2,1),x
...
x=first(matmul(a,b))
note if you want to make sure this is only used for a dimension(1,1) array you need to use an explicit interface and do:
real function first(matrix)
real, intent(in) :: matrix(:,:)
if(.not.all(shape(matrix).eq.[1,1]))reporterror()
first=matrix(1,1)
end function
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.
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).