module RandMat
implicit none
double complex, dimension(:,:), allocatable :: A, L, U
integer :: n
contains
function Diag() result(Eigenvalues)
implicit none
double complex, dimension(n) :: Eigenvalues
double complex, dimension(2*n-1) :: work
double precision, dimension(n,n) :: eigenmatrix
integer :: Lwork
integer :: info, ii
double precision :: rwork(3*n-2)
double complex :: check(n,n), alpha, beta
check=A
info=0
lwork =2*n-1
!call routine to diagonalize A
call zheev('V','U',n, check, n, Eigenvalues, work, lwork, rwork, info )
end function
end module
This function is declared within a module. A is a n x n Hermitian matrix that I defined within the module, so I can use it in here. n is defined in the module as well and is the dimension of A.
The problem is that I always get a run time error:
Intel MKL ERROR: Parameter 8 was incorrect on entry to ZHEEV
when calling zheev.
EDIT: I added the declaration of the variables in the module. The Matrix A is allocated and initialized with the following Routine:
subroutine Initialize
implicit none
integer, dimension(2) :: clock
integer , dimension(:), allocatable :: seed
integer :: ii,jj
double precision :: c,d
!create random complex matrix of dimension nxn
!-----------------------------------------------------------------------
!allocate A
if (.not.allocated(A)) then
allocate(A(N,N))
else if (size(A,1).neqv.N) then
allocate(A(N,N))
end if
!get execution time--> will be used as seed
call System_clock(count=clock(1))
call random_seed(put=clock)
!initialize matrix with random values
do ii=1,n
!since we are only interested in hermitian matrices is it enough to only !initialize the upper diagonal matrix
do jj=ii,n
call random_number(c)
Call random_number(d)
A(ii,jj)=cmplx(c, d)
end do
end do
!make matrix hermitian
!---------------------------------------------------------------------------
do ii=2, n
do jj=1, ii-1
A(ii,jj)=conjg(A(jj,ii))
end do
end do
end subroutine
Related
I am writing a code with a lot of 2D arrays and manipulation of them. I would like the code to be as concise as possible, for that I would like to use as many 'implicit' operation on array as possible but I don't really know how to write them for 2D arrays.
For axample:
DO J=1,N
DO I=1,M
A(I,J)=B(J)*A(I,J)
ENDDO
ENDDO
become easily:
DO J=1,N
A(:,J)=B(J)*A(:,J)
ENDDO
Is there a way to reduce also the loop J?
Thanks
For brevity and clarity, you could wrap these operations in a derived type. I wrote a minimal example which is not so concise because I need to initialise the objects, but once this initialisation is done, manipulating your arrays becomes very concise and elegant.
I stored in arrays_module.f90 a derived type arrays2d_T which can hold the array coefficients, plus useful information (number of rows and columns). This type contains procedures for initialisation, and the operation you are trying to perform.
module arrays_module
implicit none
integer, parameter :: dp = kind(0.d0) !double precision definition
type :: arrays2d_T
real(kind=dp), allocatable :: dat(:,:)
integer :: nRow, nCol
contains
procedure :: kindOfMultiply => array_kindOfMuliply_vec
procedure :: init => initialize_with_an_allocatable
end type
contains
subroutine initialize_with_an_allocatable(self, source_dat, nRow, nCol)
class(arrays2d_t), intent(inOut) :: self
real(kind=dp), allocatable, intent(in) :: source_dat(:,:)
integer, intent(in) :: nRow, nCol
allocate (self%dat(nRow, nCol), source=source_dat)
self%nRow = nRow
self%nCol = nCol
end subroutine
subroutine array_kindOfMuliply_vec(self, vec)
class(arrays2d_t), intent(inOut) :: self
real(kind=dp), allocatable, intent(in) :: vec(:)
integer :: iRow, jCol
do jCol = 1, self%nCol
do iRow = 1, self%nRow
self%dat(iRow, jCol) = vec(jCol)*self%dat(iRow, jCol)
end do
end do
end subroutine
end module arrays_module
Then, in main.f90, I check the behaviour of this multiplication on a simple example:
program main
use arrays_module
implicit none
type(arrays2d_T) :: A
real(kind=dp), allocatable :: B(:)
! auxilliary variables that are only useful for initialization
real(kind=dp), allocatable :: Aux_array(:,:)
integer :: M = 3
integer :: N = 2
! initialise the 2d array
allocate(Aux_array(M,N))
Aux_array(:,1) = [2._dp, -1.4_dp, 0.3_dp]
Aux_array(:,2) = [4._dp, -3.4_dp, 2.3_dp]
call A%init(aux_array, M, N)
! initialise vector
allocate (B(N))
B = [0.3_dp, -2._dp]
! compute the product
call A%kindOfMultiply(B)
print *, A%dat(:,1)
print *, A%dat(:,2)
end program main
Compilation can be as simple as gfortran -c arrays_module.f90 && gfortran -c main.f90 && gfortran -o main.out main.o arrays_module.o
Once this object-oriented machinery exists, call A%kindOfMultiply(B) is much clearer than a FORALL approach (and much less error prone).
No one has mentioned do concurrent construct here, which has the potential to automatically parallelize and speed up your code,
do concurrent(j=1:n); A(:,j)=B(j)*A(:,j); end do
A one-line solution can be achieved by using FORALL:
FORALL(J=1:N) A(:,J) = B(J)*A(:,J)
Note that FORALL is deprecated in the most recent versions of the standard, but as far as I know, that is the only way you can perform that operation as a single line of code.
Related to this question, but I believe the issue is more clearly identified with this example.
I have some legacy code that looks like this:
subroutine ID_OG(N, DETERM)
use variables, only: ID
implicit real (A-H,O-Z)
implicit integer(I-N)
DETERM = 1.0
DO 1 I=1,N
1 ID(I)=0
DETERM = sum(ID)
end subroutine ID_OG
Replacing use variables, only: ID with real, dimension(N) :: ID or real, dimension(:), allocatable :: ID causes a noticeable performance loss. Why is this? Is this expected behavior? I am wondering if it has something to do with the program needing to repeatedly allocate memory for the local array ID, while the use statement allows the program to skip the memory allocation step.
In the legacy code ID is in module variables but it is only used within the subroutine ID_OG. It is not used anywhere else in the code - it is not an input or an output. To me, it seems like good programming practice for ID to be removed from module variables and defined locally in the subroutine. But perhaps that isn't the case.
Minimum working example (MWE):
compiling as gfortran -O3 test.f95 using gfortran 8.2.0
MODULE variables
implicit none
real, dimension(:), allocatable :: ID
END MODULE variables
program test
use variables
implicit none
integer :: N
integer :: loop_max = 1e6
integer :: ii ! loop index
real :: DETERM
real :: t1, t2
real :: t_ID_OG, t_ID_header, t_ID_no_ID, t_OG_no_ID, t_allocate
character(*), parameter :: format_header = '((A5, 1X), 20(A12,1X))'
character(*), parameter :: format_data = '((I5, 1X), 20(ES12.5, 1X))'
open(1, file = 'TimingSubroutines_ID.txt', status = 'unknown')
write(1,format_header) 'N', 't_Legacy', 't_header', 't_head_No_ID', 't_Leg_no_ID', &
& 't_allocate'
do N = 1, 100
allocate(ID(N))
call CPU_time(t1)
do ii = 1, loop_max
CALL ID_OG(N, DETERM)
end do
call CPU_time(t2)
t_ID_OG = t2 - t1
print*, N, DETERM
call CPU_time(t1)
do ii = 1, loop_max
CALL ID_header(N, DETERM)
end do
call CPU_time(t2)
t_ID_header = t2 - t1
print*, N, DETERM
call CPU_time(t1)
do ii = 1, loop_max
CALL ID_header_no_ID(N, DETERM)
end do
call CPU_time(t2)
t_ID_no_ID = t2 - t1
print*, N, DETERM
call CPU_time(t1)
do ii = 1, loop_max
CALL ID_OG_no_ID(N, DETERM)
end do
call CPU_time(t2)
t_OG_no_ID = t2 - t1
print*, N, DETERM
call CPU_time(t1)
do ii = 1, loop_max
CALL ID_OG_allocate(N, DETERM)
end do
call CPU_time(t2)
t_allocate = t2 - t1
print*, N, DETERM
deallocate(ID)
write(1,format_data) N, t_ID_OG, t_ID_header, t_ID_no_ID, t_OG_no_ID, t_allocate
end do
end program test
subroutine ID_OG(N, DETERM)
use variables, only: ID
implicit real (A-H,O-Z)
implicit integer(I-N)
DETERM = 1.0
DO 1 I=1,N
1 ID(I)=0
DETERM = sum(ID)
end subroutine ID_OG
subroutine ID_header(N, DETERM)
use variables, only: ID
implicit none
integer, intent(in) :: N
real, intent(out) :: DETERM
integer :: I
DETERM = 1.0
DO 1 I=1,N
1 ID(I)=0
DETERM = sum(ID)
end subroutine ID_header
subroutine ID_header_no_ID(N, DETERM)
implicit none
integer, intent(in) :: N
real, intent(out) :: DETERM
integer :: I
real, dimension(N) :: ID
DETERM = 1.0
DO 1 I=1,N
1 ID(I)=0
DETERM = sum(ID)
end subroutine ID_header_no_ID
subroutine ID_OG_no_ID(N, DETERM)
implicit real (A-H,O-Z)
implicit integer(I-N)
real, dimension(N) :: ID
DETERM = 1.0
DO 1 I=1,N
1 ID(I)=0
DETERM = sum(ID)
end subroutine ID_OG_no_ID
subroutine ID_OG_allocate(N, DETERM)
implicit real (A-H,O-Z)
implicit integer(I-N)
real, dimension(:), allocatable :: ID
allocate(ID(N))
DETERM = 1.0
DO 1 I=1,N
1 ID(I)=0
DETERM = sum(ID)
end subroutine ID_OG_allocate
Allocating the arrays takes time. The compiler is free to allocate the local arrays where-ever it wants, but it can typically be adjusted by compiler-specific flags. Use -fstack-arrays for gfortran to force local arrays to stack.
Allocating on the stack is just changing the stack pointer, it is virtually for free. Allocating on the heap, however, is more involved and requires some bookkeeping.
There are situations where local variables are in order and there are situations where global (module) variables are in order. One can also use local saved variables or variables that are components of some objects. One cannot say which one is better without seeing the complete design of the code in question.
FWIW, with -fstack-arrays I do not see much difference except when allocating explicitly using allocate():
Explicit allocate will always use the heap.
Without -fstack-arrays I do see some:
The graphs are quite noisy because my notebook is running many processes at the same time.
This is not to say that one should always use -fstack-arrays, I used to demonstrate the difference. The option is useful, but care must be taken to avoid a stack overflow error. -fmax-stack-var-size may help with that.
As your tests are pointing out, the additional overhead of all methods which do not use the module variable is due to the language's phylosophy to not bother the user with memory handling too much.
The compiler will decide where memory should be allocated, unless you start tinkering with compiler flags. You see allocation/freeing time as a drawback, but your analysis also shows:
stack vs. heap memory handling overhead quickly gets smaller and smaller: for N>=100, it is already <50%. a dimension(100) array is a ridiculously small memory chunk on a modern computer.
declaring a variable in a module just for speeding up storage is a Fortran 90 way of making it a global, and as such, it is a deprecated coding style.
I think the best strategy to make the code well-coded and fast is:
Is N going to be constant through the whole runtime? Then, it would be a good idea to encapsulate it into a class:
module myCalculation
implicit none
type, public: fancyMethod
integer :: N = 0
real, allocatable :: ID(:)
contains
procedure :: init
procedure :: compute
procedure :: is_init
end type fancyMethod
contains
elemental subroutine init(self,n)
class(fancyMethod), intent(inout) :: self
integer, intent(in) :: n
real, allocatable :: tmp(:)
self%N = n
allocate(tmp(N)); tmp(:) = 0
call move_alloc(from=tmp,to=self%ID)
end subroutine init
elemental logical function is_init(self)
class(fancyMethod), intent(in) :: self
is_init = allocated(self%ID) .and. size(self%ID)>0
end function is_init
real function compute(self,n,...) result(DETERM)
class(fancyMethod), intent(inout) :: self
integer, intent(in) :: n
....
if (.not.is_init(self)) call init(self,N)
DETERM = sum(self%ID(1:N))
end function compute
end module myCalculation
Is N going to be constant and small? Why not just use a PARAMETER to define its max size? if it is a parameter, the compiler will perhaps always put the automatic array on the stack:
real function computeWithMaxSize(N) result(DETERM)
integer, intent(in) :: N
integer, parameter :: MAX_SIZE = 1024
real :: ID(MAX_SIZE)
[...]
if (N>MAX_SIZE) stop ' N is too large! '
DETERM = sum(ID(1:N))
end function computeWithMaxSize
Is N going to be variable-sized and large? Then, the in-routine memory handling is fine, and its overhead is likely negligible, because the CPU time will be dominated by the calculation; use an allocatable version if you're not sure that the size can be so large to cause any stack issues:
real function computeWithAllocatable(N) result(DETERM)
integer, intent(in) :: N
real, allocatable :: ID(:)
allocate(ID(N))
[...]
DETERM = sum(ID(1:N))
end function computeWithAllocatable
I expected the intrinsic matmul to fail when multiplying non-conforming matrices. In this simple example (see a simple code below), I am multiplying a 4x3 matrix by 4x4 matrix using matmul. Interestingly the intel compiler does not issue any warning or fatal error message at either the run-time or compile time. I tried '-check all' flag and it did not catch this error, either. Does anyone have any thoughts on this?
P.S. gfortran does complain about this operation
program main
implicit none
interface
subroutine shouldFail(arrayInput, scalarOutput)
implicit none
real (8), intent(in) :: arrayInput(:,:)
real (8), intent(out) :: scalarOutput
end
end interface
real (8) :: scalarOutput, arrayInput(4, 3)
arrayInput(:,:) = 1.0
call shouldFail(arrayInput, scalarOutput)
write(*,*) scalarOutput
end program main
!#############################################
subroutine shouldFail(arrayInput, scalarOutput)
implicit none
real (8), intent(in) :: arrayInput(:,:)
real (8), intent(out) :: scalarOutput
real (8) :: jacobian(3, 4), derivative(4, 4)
derivative(:,:) = 1.0
jacobian = matmul(arrayInput, derivative)
scalarOutput = jacobian(1, 2)
end subroutine shouldFail
On Intel REAL(8) is consistent, so moving onto the question...
The answer is here: software.intel.com/en-us/node/693211
If array input has shape (n, m) and derivative has shape (m, k), the result is a rank-two array (Jacobite) with shape (n, k)...
Whether one needs/want to add in USE ISO_C_BINDING and then using REAL(KIND=C_FLOAT) can be worthwhile for some conceptual future where it need to be portable... (Usually after the MATMUL is convincing one that it works).
It could look like this:
subroutine shouldFail(arrayInput, scalarOutput)
USE ISO_C_BINDING
implicit none
real(KIND=C_DOUBLE), DIMENSION(:,:), intent(in ) :: arrayInput
real(KIND=C_DOUBLE) , intent( out) :: scalarOutput
real(KIND=C_DOUBLE), DIMENSION(3,4) :: Jacobian
real(KIND=C_DOUBLE), DIMENSION(4,4) :: derivative
(Here: You may want to consider checking the rank/shape before the MATMUL call if you are concerned.)
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 have a few array variables in a module that are dynamic, and later allocated in one of two subroutines outside of the module. However, in one subroutine I want the array to be 1D, in the other subroutine I want it to be 2D.
In principle I would want something like this in the module, but I don't believe this is possible in the declaration area?:
if (option1) then
real (kind=8), allocatable :: arr1(:)
else
real (kind=8), allocatable :: arr1(:,:)
endif
Is there a way in the allocatable declarations to have the dimension be dynamic?
Edit1: The reason I'm doing this is I'm adding a new subroutine to an existing codebase, but I want to be backwards compatible. arr1 is only used by the two separate subroutines, the main program doesn't use it at all. Here is some more complete code showing the idea:
program myprog
use inputs
call read_inputs
if (option1) then
call do1
else
call do2
endif
contains
subroutine read_inputs
use inputs
use mymod
!!!read from file .logical. option1, integers N1, N2
!allocate arrays
if (option1) then
else
endif
end subroutine read_inputs
subroutine do1
use inputs
use mymod
allocate(arr1(N1))
!do stuff with arr1
end subroutine do1
subroutine do2
use inputs
use mymod
allocate(arr1(N1,N2))
!do stuff with arr1
end subroutine do2
end program
module inputs
logical :: option1
integer :: N1, N2
end module inputs
module mymod
use inputs
!!!!can I do something here to make the rank of arr1 dynamic? I don't think the following will work
if (option1)
real (kind=8), allocatable :: arr1(:)
else
real (kind=8), allocatable :: arr1(:,:)
endif
end module mymod
I may just have two separate variable in mymod, arr1 and arr1_new. I was just hoping to avoid that.
I think the 'olden' ways to do something like this is to pass the first element instead of the whole array and the size of the array separately:
program dyn_array
implicit none
integer :: a(2, 3)
integer :: i
call set1d(a(1,1), size(a))
do i = 1, 3
write(*, '(2I4)') a(:,i)
end do
contains
subroutine set1d(array, s)
implicit none
integer, intent(in) :: s
integer, intent(out) :: array(s)
integer :: i
do i = 1, s
array(i) = 3 * i
end do
end subroutine set1d
end program dyn_array
"Can you pass a 2D array into a subroutine that expects a 1D array and get the right size?"
You can use reshape, but if you code is relying on the compiler to help then a 2D into a 1D is dicey. You could use RESHAPE before and after... Or you can have 2 routines, which we can call set1d and set2d.
Then in the module you can have it choose what one you want to use. You could have integer(s), float(s), complex(s), byte.
You would CALL Array$Set(Array,s)
MODULE Stuff
PUBLIC :: Array$Set
PRIVATE
INTERFACE Array$Set
MODULE PROCEDURE Set1d_Float, Set1D_Double, set2D_Float, Set2D_Double
END INTERFACE Array$Set
CONTAINS
SUBROUTINE Set1D_Float(Array,S)...
!$DIR ATRIBUTES ASUUME_ALIGND:64 :: Array
REAL, DIMENSION(:,:), CONTIGUOUS, INTENT(INOUT) :: Array
REAL, DIMENSION(:,:), INTENT(IN ) :: S
REAL, DIMENSION(2) :: Shapez
...
Shapez = Shape(Array)
DO I = 1, Shapez(1)
DO J = 1, Shapez(2)
...
END SUBROUTINE Set1D_Float
END MODULE Stuff
For your example:
if (option1) then
real (kind=8), allocatable :: arr1(:)
else
real (kind=8), allocatable :: arr1(:,:)
endif
I would suggest this:
!DIR ATTRIBUTES ALIGN:64 :: Arr1
REAL, DIMENSION(:), ALLOCATABLE :: Arr1
...
if (option1) then
ALLOCATE(Arr1(<#>))
else
ALLOCATE(Arr1(<#>*<#2>))
RESHAPE(Arr1, SHAPE=/(#1,#2)) !Check the syntax
endif
CALL Array$Set(Arr1,s) !It'll find the right one...
!... at the bottom ...
IF(ALLOCATED(Arr1)) DEALLOCATE(Arr1)
END PROGRAM