Silverfrost Forums

Welcome to our forums

Problem: “Problem Array Section with /Checkmate”

17 Nov 2009 5:43 #5377

The attached program looks terrible. However, it reflects the structure of a “real” program, where I found the problem. The problem occurs when running the program with the /Checkmate option, which I almost always use:

ftn95 Problem_Array_Section.f95 /Checkmate /underflow /Link >> comp.lis sdbg Problem_Array_Section.exe

The error message is “Attempt to call a routine with argument number two containing too few array elements”

The reason why I chose this form is because in the real problem the indices of array x (1,2,3..) in subroutine SubB correspond to indices 2,3,4 of array Data in subroutine SubA. For this transformation I need the array section as argument.

Is anything wrong with the program? Can anybody comment on this problem?

Many thanks,

Klaus Lassmann [ Winapp

Program Problem_Array_Section

Implicit None
Integer                :: i, n

Real , Dimension (400) :: Result

n = 3

! Subroutine SubA calculates with the help of SubB the field Result

Call SubA ( Result, n )

! #########

Write (*,*)   ' Result (1,n)= ', ( Result (i), i=1,n )

Contains

Subroutine SubA (Data, m)

  Implicit None
  Integer                            :: i
  Integer             , Intent (in ) :: m
  Real , Dimension (m), Intent (out) :: Data

! --- This call leads to an error when compiled with /Checkmate:

  Call SubB ( m, Data (1:m) )

! --- This call seems to be OK

! Call SubB ( m, Data )

  Write (*,*) ' Data (1,m) = ', ( Data (i), i=1,m )

End Subroutine SubA 

End Program Problem_Array_Section

! --------------------------------------------------------------------

Subroutine SubB ( k,x  )

  Implicit None
  Integer             , Intent (in ) :: k
  Real , Dimension (k), Intent (out) :: x
  Integer                            :: i

  Do i = 1, k
    x(i) = i**2
  End Do

End Subroutine SubB

]

17 Nov 2009 10:58 #5379

Paul, This second example shows that errors associated with array sections in subroutine arguments when compiled with /check are not limited to automatic arrays. The program works ok with /debug. As I have found before, if I use array sections, I can not debug using /check. Can this be investigated ? Thanks John

18 Nov 2009 8:39 #5380

Yes this looks like a problem I need to look at but I may not be able to give you a quick response at the moment.

19 Nov 2009 8:02 #5386

I have identified the problem and expect to be able to fix it in one way or another.

The only short term solution is to use /inhibit_check 5 on the command line. You could put subB in a separate file in order to apply this only to the subroutine.

19 Nov 2009 8:50 #5387

Paul,

thank you very much for your quick reaction. For me it is easy to circumvent this problem. I guess that this problem will be solved with the next update and I will wait for it.

Best wishes,

Klaus

3 Dec 2009 11:15 #5473

Paul,

Any update on this, as this bug has limited my use of both /check, SDBG and array sections for a number of years. It would be a good one to clean up.

John

4 Dec 2009 8:10 #5474

I have been working on this but it is one of those tricky ones. I am reasonably hopeful that it will be fixed for the next release.

5 Dec 2009 12:22 #5476

I'm not sure why you ever need to use the form

Call SubB ( m, Data (1:m) )

If you are passing m as a variable, then the range (1:m) is redundant.

If you argue that you may want to pass another section of the array such as Data(i:j) then this can be done as:

i=5 j=10 m=j-i+1 call SubB(m,data(i))

for example if i=5 and j=10, then the following elements are required in the subroutine: 5, 6, 7, 8, 9, 10 which is six and m=10 - 5 + 1 = 6. This statement then passes 6 elements of array 'data' starting with the 5th.

All you are passing for an array is the address of the start element and if that is the very first element, i.e. data(1) or data(1,1) then you only need to pass 'data' and this you have done in the part of your example that worked. Even the shape of the array can be different from one program segment to another, but care is required if it is of rank 2 or higher.

The only reason that you might want to pass data(1:m) is if you also do not pass m and use a dummy dimension and the size finction to determine the passed size. See below:

winapp
integer*4 data1(10), i

do i=1,10
  data1(i) = i
enddo


call sub1(data1(2:4))


end


subroutine sub1(idata)
integer*4 idata(*),isize
isize = size(idata)

print *,'array contains ',isize,' elements'

print *,(idata(i),i=1,isize)

end 

When compiling this with only /debug, the size of the array in the subroutine seems to be 538976289, which is ridiculous.

By adding /check, it partially works. I would have thought that it should work without the need to use any specific compiler switch. This might be in the documentation somewhere, but if not, it should be.

What happens is that the 'size' function returns 9, which IS the size of the array starting at position2 to the end, i.e. position 10. I would have thought that it should have been of size 3.

I seems to be safer to go back to the old fashioned method as shown in sub2

winapp
integer*4 data1(10), i, m

do i=1,10
  data1(i) = i
enddo


m=4 - 2 + 1

call sub2(data1(2),m)


end



subroutine sub2(idata,m)
integer*4 idata(m),isize
isize = size(idata)

print *,'array contains ',isize,' elements'

print *,(idata(i),i=1,isize)

end 

Comments please!!

Ian

5 Dec 2009 10:15 #5477

Ian,

I have code which is of the form

call ends (data(6:7,n))
...
subroutine ends (end_data)
integer*4 end_data(2)
...

If I compile this with /debug, I think there is no problem, but if I compile it with /check, I get an error that the size of array is wrong. I can always overcome it by changing the code with a temporary array or not using /check. Unfortunately it is in a development project/hobby which at present I don't have much time to complete, so will have to wait till the bug goes. I've been trying to clean out old code patches and be more Fortran 95+ consistent.

John

5 Dec 2009 11:00 #5478

Hi John,

I assume that data is a two dimensional array and that your intent is that the subroutine deals with the two elements in positions 6 & 7 on row n.

I think that it is only necessary to pass data(6,n) to the subroutine and as it is dimensioned to two in the subroutine, then it is treated within as a one dimensional array of two elements.

I have never understood the need for the array range format described whilst passing the info. I only use the start:end format for the dimensioning of arrays where the default start element is not 1.

But of course I'm an old fashioned guy and I might have missed the introduction of this method. Multi dimensional arrays can be tricky to pass by the method described if one is hoping to refer to a small rectangular section which does not start at (1,1,1...) That is

consider a matrix containing values thus:
i----->
j   a b c d e f
|   g h i j k l 
|   m n o p q r
v

This is stored in memory as a linear block of memory containing:
a b c d e f g h i j k l m n o p q r

Trying to pass the sub-array i j and o p using

call sub(data(3,2))

with subroutine sub(data) dimension data(2,2)

will just end up with the data in the subroutine containing: i j k l

That is, the call references the start element and the dimension of 2,2 takes the next 4 elements.

Using the start:end format for two-dim arrays would only make sense if one could pass:

data(3:4,2:3)

but then the compiler would have to extract the appropriate elements where it knows the original shape of the array, and reform these into separate linear storage and pass the pointer to this new grouping to the routine. On return, it would then have to reverse the process and set any new values which may have been changed in the subroutine. This is very messy, and I just don't think that Fortran does that. The time overhead would be excessive for large multi dimensional arrays.

Please confirm

Regards

Ian

6 Dec 2009 12:57 #5480

Ian,

My understanding is that when providing 'data(3:4,2:3)' as an argument, then the compiler will provide a temporary array(2,2) and update it when it returns. I tested this on a recent post (see general>use of type arrays) by changing the argument, while the original array did not change, but was updated on return. It's always difficult to stop using old Fortran, when it worked so well.

John[/quote]

6 Dec 2009 9:05 #5481

John,

Wow - that is useful, I shall now have to rewrite all my software even though it works, well some of it works.

Test program where the array elements contain the subscripts in the form i*1000+j and the test subroutine negates the value so you can see the modification to the main routine data.

winapp
integer*4 i,data2(10,10),i1,i2,j1,j2,m12,n12

!set some data
do i=1,10
  do j=1,10
    data2(i,j) = i*1000 + j
  enddo   
enddo

!show the original contents
print *,'before call main data2 array contains '
do j=1,10
  print '(10i5)',(data2(i,j),i=1,10)
enddo
!define the sub array and its dimensions
i1  = 3
i2  = 6
m12 = i2 - i1 + 1
j1  = 2
j2  = 8
n12 = j2 - j1 + 1

!call  the test subroutine to negate the sub matrix values
call sub3(data2(i1:i2,j1:j2),m12,n12)

!display result of the action on the full matrix
print *,'after  call main data2 array contains '
do j=1,10
  print '(10i5)',(data2(i,j),i=1,10)
enddo

end

subroutine sub3(idata,m,n)
integer*4 idata(m,n),i,j,m,n

print *,'passed array contains '

do j=1,n
  print *,(idata(i,j),i=1,m)
  do i=1,m
    idata(i,j) = -idata(i,j)
  enddo
enddo
end

Thanks John!

Ian

6 Dec 2009 9:50 #5482

But of course, I wan't happy about having to pass the dimensions, so using modules, interfaces and colons gives:

winapp
!create a module with interfaces for subroutines where necessary
module interfaces
INTERFACE
  SUBROUTINE SUB4(idata)
    INTEGER idata(:,:)
  END SUBROUTINE
END INTERFACE
end module interfaces

! add this statement wherever one of these subroutine is to be called
use interfaces

! create the test data
integer*4 i,j,data2(10,10),i1,i2,j1,j2
do i=1,10
  data1(i) = i
  do j=1,10
    data2(i,j) = i*1000 + j
  enddo   
enddo

! setup the range of the sub matrix
i1  = 3
i2  = 6
j1  = 2
j2  = 8

!  check the data content
print *,'before call main data2 array contains '
do j=1,10
  print '(10i5)',(data2(i,j),i=1,10)
enddo

! call the subroutine
call sub4(data2(i1:i2,j1:j2))

! show the changesprint *,'after  sub4 call main data2 array contains '
do j=1,10
  print '(10i5)',(data2(i,j),i=1,10)
enddo

end

subroutine sub4(idata)
! use blank place holders to show passed array is rank 2
integer*4 idata(:,:),i,j,m,n

m=size(idata,1)
n=size(idata,2)
print *,'array dimensions are',m,n
print *,'passed array contains '
do j=1,n
! show the sub matrix as passed
  print *,(idata(i,j),i=1,m)
! make some changes
  do i=1,m
    idata(i,j) = -idata(i,j)
  enddo
enddo
end

Now I understand, what you tried to say to me! And how I suffered for my sanity. etc. Sorry about the corrupted song lyrics

And the following is also possible:

call sub4(data2(i1:i2,j1:j2:2))

Only alternate rows from data2 are passed, i.e. j1=start row, j2 = finish row and 2 is the row increment.

Regards

Ian

6 Dec 2009 12:19 #5483

Ian,

I changed your program, using a module to access the original data2 array. The changed program shows (to me) that there must be a copy of the sectioned array data2, being used in sub4. It would appear that all sections are placed on the stack or heap, so we should be careful when specifying sections of large arrays, for example with dot_product (a(i1:i2), b(j1:j2)); this could be very inefficient, in comparison to vecsum (a(i1), b(j1), i2-i1+1), when i2-i1 is large. I have made this mistake in the past! and now realise why the newish code did not work well.

!create a module with interfaces for subroutines where necessary 
module all_data
integer*4 data1(10), data2(10,10)
end module all_data

module interfaces 
INTERFACE 
  SUBROUTINE SUB4(idata) 
    INTEGER idata(:,:) 
  END SUBROUTINE 
END INTERFACE 
end module interfaces 

! add this statement wherever one of these subroutine is to be called 
use all_data
use interfaces 

! create the test data 
integer*4 i,j
!
do i=1,10 
  data1(i) = i 
  do j=1,10 
    data2(i,j) = i*1000 + j 
  enddo    
enddo 

!  check the data content 
print *,'before call main data2 array contains ' 
do j=1,10 
  print '(10i6)',(data2(i,j),i=1,10) 
enddo 

! call the subroutine 
call sub4 ( data2(3:6,2:8) ) 

! show the changesprint *,'after  sub4 call main data2 array contains ' 
print *,'original array after return from sub4 contains ' 
do j=1,10 
  print '(10i6)',(data2(i,j),i=1,10) 
enddo 

end 

subroutine sub4 (idata) 
use all_data
! use blank place holders to show passed array is rank 2 
integer*4 idata(:,:),i,j,m,n 

m=size (idata,1) 
n=size (idata,2) 
print *,'array dimensions are',m,n 
print *,'passed array contains ' 
do j=1,n 
! show the sub matrix as passed 
  print *,(idata(i,j),i=1,m) 
! make some changes 
  do i=1,m 
    idata(i,j) = -idata(i,j) 
  enddo 
enddo 
!
print *,'passed array after changes contains ' 
do j=1,n 
! show the sub matrix as passed 
  print *,(idata(i,j),i=1,m) 
enddo 
!
print *,'original array after changes contains ' 
do j=1,10 
  print '(10i6)',(data2(i,j),i=1,10) 
enddo 
end 
7 Dec 2009 2:02 #5489

Ian, I further changed the program to test both 2 dimension arrays and 1 dimension vectors. Based on the results, it may appear that the sub section is generated if the required array is not continuous in memory, as the 1 dimensional vector appears to change.

!create a module with interfaces for subroutines where necessary 
module all_data 
integer*4 data1(10), data2(10,10) 
end module all_data 

module interfaces 
INTERFACE 
  SUBROUTINE SUB4(jdata, idata) 
    INTEGER jdata(:), idata(:,:) 
  END SUBROUTINE 
END INTERFACE 
end module interfaces 

! add this statement wherever one of these subroutine is to be called 
use all_data 
use interfaces 

! create the test data 
  integer*4 i,j 
! 
  do i=1,10 
    data1(i) = i 
    do j=1,10 
      data2(i,j) = i*1000 + j 
    enddo    
  enddo 

!  check the data content 
  print *,'before call main 10x10 array contains ' 
  do j=1,10 
    print '(10i6)',(data2(i,j),i=1,10) 
  enddo 

! call the subroutine 
  call sub4 ( data1(3:6), data2(3:6,2:8) ) 

! show the changes
  print *,'original 10x10 array after return from sub4 contains ' 
  do j=1,10 
    print '(10i6)',(data2(i,j),i=1,10) 
  enddo 

  print *,'original vector data1 after return from sub4 contains ' 
  print '(10i6)',(data1(i),i=1,10) 

end 

subroutine sub4 (jdata, idata) 
use all_data 
! use blank place holders to show passed array is rank 2 
  integer*4 jdata(:), idata(:,:),i,j,m,n 
!
  m=size (idata,1) 
  n=size (idata,2) 
  print *,'array idata dimensions are',m,n 
  print *,'passed array contains ' 
  do j=1,n 
    print '(10i6)',(idata(i,j),i=1,m) 
! make some changes 
    do i=1,m 
      idata(i,j) = -idata(i,j) 
    enddo 
  enddo 
! 
  print *,'passed array after changes contains ' 
  do j=1,n 
    print '(10i6)',(idata(i,j),i=1,m) 
  enddo 
! 
  print *,'original 10x10 array after changes contains ' 
  do j=1,10 
    print '(10i6)',(data2(i,j),i=1,10) 
  enddo 
!
  m=size (jdata) 
  print *,'vector jdata dimension is',m
  print *,'passed vector contains ' 
  print '(10i6)',(jdata(i),i=1,m) 
! make some changes to vector
  do i=1,m 
    jdata(i) = -jdata(i) 
  end do 
! 
  print *,'passed vector after changes contains ' 
  print '(10i6)',(jdata(i),i=1,m) 
! 
  print *,'original vector after changes contains ' 
  print '(10i6)',(data1(i),i=1,10) 
  print *,'NOTE ORIGINAL VECTOR HAS CHANGED, SO NO TEMPORARY VECTOR !!'
!
end 

So my past experience with inefficiencies associated with sections of the form 'data1(i1:i2)' are not due to temporary arrays.

John

ps : I also tried this program in SDBG, but got the asembler code window, rather than the source code to display. Don't know why. Linked with 'ftn95 ian.f95 /check /link'. I still can't drive SDBG !!

7 Dec 2009 7:34 #5500

How about adding a /debug

7 Dec 2009 11:43 #5505

Ian,

I tried including /debug, but no change. Just to check, I tried another small program example with /check, then SDBG and it started perfectly with the fortran code window, so I'm not sure why this example above starts with the assembler code window. Does anyone know why this occurs ?

John

PS : It's also a pain that the module 'interfaces' can not be used in SUB4. If it was, then we could have a module interfaces, which declares the interface for all subroutines and functions used in a program. This could then be used by the compiler to check for errors in argument lists for all uses of each routine. Otherwise there is never any checking that the INTERFACE defined is the same as in the coded routine. We still have 2 seperate definitions of the interface, which is a major deficiency in the INTERFACE concept in Fortran.

8 Dec 2009 7:19 #5510

John, Create a separate .f95 file containing the module and interface data. Compile it to produce the all_data.mod Compile and link the main code part. SDBG then opens the code window.

I don't see why this should be the case, - is it an error?

Ian

4 Feb 2010 11:26 #5888

Paul,

Any chance of a fix to this error : 'array sections not working with /check, for some types of arrays' to be included in the next release.

This has been an annoying bug to me for many years.

John

5 Feb 2010 10:58 #5906

Sorry but this bug is still outstanding. Thank you for reminding me. The current work around is to add /inhibit_check 5.

Please login to reply.