Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Pointer on Array - pointer and target same size but different shape.

BRAHIM__ADJEROUD
Beginner
3,778 Views

Hello

I have a little problem with array.

I would like to be able to access it according to coordinates relating to a table of rank 3 AND also according to coordinates of table of rank 1.

Here is an example below:

integer, dimension(2,2,2) , target:: Int_array_xyz
integer, dimension(8), pointer:: Int_array_xxx

Int_array_xyz = RESHAPE((I,I=1,8),(/2,2,2/))

Int_array_xxx(1) => Int_array_xyz(1,1,1)

________________________________________

This approach is not accepted.

It is also not possible to do the following:

integer, dimension(2,2,2) :: Int_array_xyz
integer, dimension(2) :: Int_array_xxx
EQUIVALENCE (Int_array_xxx(1) , Int_array_xyz(1,1,1) )

________________________________________

I’m trying to improve a computational tool, and for some reason, the previous developer chose to reference some values in a 3-dimensional space, and other values in a 1-dimensional space.

When someone wishes to perform matrix operations by combining certain values, we are obliged to carry out an expensive and dangerous calculation of index matches.
If I could access to some of my table values alternately according to the two index systems (1 dimension and 3 dimensions) I would save a lot of development time.

I thought about using the RESHAPE function to be able to switch from a 3D system to a 1D system, but it is not possible to make a RESHAPE on itself!


 

XXX(:,:,:) = RESHAPE(XXX(:),(/2,2,2/))

So I have to create two tables. So that I double the memory space used, and also I need to synchronize the tables when one or the other is modified.
 

XYZ(:,:,:) = RESHAPE(XXX(:),(/2,2,2/))

________________________________________

Does anyone have any ideas?
Is that possible? Do I have to look further, on the side of "type" or "class"?

Thank you

0 Kudos
51 Replies
BRAHIM__ADJEROUD
Beginner
2,580 Views

I have also try by using the PACK instruction, but not with any success.

Does anyone have an idea. 

        INTEGER,  DIMENSION (10,3)          :: MAT_XY
        INTEGER,  DIMENSION (30)            :: PACK_XX
        LOGICAL,  DIMENSION (10,3)          :: MASK
 
 
!        PACK_XXX(:) = 0       ! for further try
 
        MASK(:,:) = .TRUE.
 
        MAT_XY=RESHAPE((/ (i,i=1,30) /),(/10,3/))
 
        PACK(MAT_XY,MASK,PACK_XX)
!         PACK(MAT_XY,.TRUE.,PACK_XX)  ! I think, this is supposed to also work

 

Thanks

0 Kudos
ArvindS_Intel
Employee
2,580 Views

Hi Adjeroud,

Thank you for raising this query. As your query has reached inappropriate forum, I will help you in routing this topic to the correct forum for better discussion. We appreciate your patience.

Regards,

Arvind

Intel Developer Zone Support

 

0 Kudos
BRAHIM__ADJEROUD
Beginner
2,580 Views

S R, Arvind (Intel) wrote:
I will help you in routing this topic to the correct forum for better discussion.

Thanks Arvind !

Et voila !

      PROGRAM ESSAI_POINTER

!!----------------------------------------------------------------------
!!                  ***  PROGRAM ESSAI_POINTER  ***
!!
!! ** PURPOSE :  LITTLE EXERCISE ON POINTER USES
!!
!!----------------------------------------------------------------------
      IMPLICIT NONE
      INTEGER,  DIMENSION (10,3) , TARGET  :: MAT_I
      INTEGER,  DIMENSION (:)    , POINTER :: AD1_MAT_I
      MAT_I(:,:)=0
      AD1_MAT_I(1:30) => MAT_I(:,:)

      MAT_I(:,:)=0
      AD1_MAT_I(5:11)=3

      print *,MAT_I(1:5 ,1)
      print *,MAT_I(6:10,1)
      print *,MAT_I(1:5 ,2)
      print *,MAT_I(6:10,2)
      print *,MAT_I(1:5 ,3)
      print *,MAT_I(6:10,3)

      MAT_I(3,:) = 2
      print *,""
      print *,AD1_MAT_I

      END PROGRAM ESSAI_POINTER

 

Not after the first try, but finally, I'm now able to do exactly what i need, with both method ! Pointer et EQUIVALENCE.

Thanks everyone for your support.

 

0 Kudos
Steve_Lionel
Honored Contributor III
2,580 Views

EQUIVALENCE is bad - don't use it in new code. As you found, you can use pointer bounds remapping.

0 Kudos
BRAHIM__ADJEROUD
Beginner
2,580 Views

Thank you Dr.  I really appreciate this kind of lighting! Moreover, EQUIVALENCE seems to be easier to use ...
So, I really enjoy not making bad choices in my early development.

for my knowledge, that it is the bad point of using EQUIVALENCE ?

0 Kudos
Steve_Lionel
Honored Contributor III
2,580 Views

EQUIVALENCE has been deemed obsolete in the standard. Its use can encourage errors and hard-to-maintain applications. There are almost always better ways to accomplish what EQUIVALENCE does.

0 Kudos
LRaim
New Contributor I
2,580 Views

But the EQUIVALENCE has deep roots in the assembler language i.e associate the same address to different variables and is very simple to understand.

The RESHAPE statement is in my opinion absolutely obscure: which other language has a similar function ? 

People in charge of defining Fortran standard, who have made such a fuss with POINTER, TARGET, and  so on ..., are continuing to mark as obsolete useful features and add new features uselessly complicated. 

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,580 Views

Steve,

>>EQUIVALENCE has been deemed obsolete in the standard. Its use can encourage errors and hard-to-maintain applications.

Then what is the functional difference with: pointer(...) => RESHAPE(other(...)) or the ASSOCIATE equivalent?

To me, the pointer route is not only confusing, and complicated (Luigi's sentiments), but from my viewpoint is more "hard-to-maintain".

My reasoning is the EQUIVILENCE is placed with the variable declarations. Thus providing some indication that the variable/array is multi-purposed as well as how it is repurposed, whereas using a pointer hides this multi-purposes-ness from all places except where it is used. IOW pointer can tend towards being blindsided. I suppose one could argue that the foundation array having TARGET provides the heads up to some extent for pointer=> but no such heads up for ASSOCIATE.

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
2,580 Views

I would say that most users of EQUIVALENCE don't understand how it works and aren't aware of the rules the compiler assumes are valid. For example, if you EQUIVALENCE an INTEGER and a REAL, and define the INTEGER, the REAL becomes undefined. The typical use of EQUIVALENCE in the past was to save storage by overlaying variables on the same memory location. It was never intended to be used for reinterpreting a value. I have seen programs misuse EQUIVALENCE and COMMON in ways that the standard prohibits and then the programmers complain when the code no longer works due to an optimization.

Pointer bounds remapping is the proper solution for this case. Some of the other substitutes for EQUIVALENCE are clunky, such as TRANSFER, but the meaning is clear and both the user and the compiler are clear as to what is happening.

I don't get the gripe about ASSOCIATE.

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,580 Views

>>I don't get the gripe about ASSOCIATE

While ASSOCIATE does not provide for re-typing a variable/array, it does provide for repurposing the, or part of the, variable/array.
The non-portable UNION can also do the same. There are many cases in programming where it is expedient to represent the same memory location(s) as differing types. Reading data files form old systems with non-conforming data layouts.

Jim Dempsey

0 Kudos
Steve_Lionel
Honored Contributor III
2,580 Views

ASSOCIATE can do much more than UNION or EQUIVALENCE. It can create arrays that are discontiguous, but it's mainly intended as an aid to clarity.

0 Kudos
andrew_4619
Honored Contributor II
2,580 Views

I eliminated equivalence from all my code many tears ago, I found it had been used in some very cludgy ways and was a source of some unpredictable results. Irrespective of if you like it or not, It  has been declared obsolete and thus it is not wise to be developing new code that uses it. In my world all new code should only be compiled with standards checking on. Simillarly  I won't use a circular saw without the safety guard ot drive my car without a seat belt.

 

0 Kudos
LRaim
New Contributor I
2,580 Views

Not using EQUIVALENCE that has a deep root in the assembler language can prevents you to create efficient software architectures.

Coding is a bit different thing. 

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,580 Views

>>ASSOCIATE can do much more than UNION or EQUIVALENCE...
Correct, however, it cannot retype a variable/array/array section, or re-layout a record, whereas a UNION (or a "cast") can.

Jim Dempsey

0 Kudos
BRAHIM__ADJEROUD
Beginner
2,580 Views

Thank you for all your answers, which I read with great interest.
Your opinions seem to differ. So here is how I construct my conclusion:

jimdempseyatthecove (Blackbelt) wrote:
[…] My reasoning is the EQUIVALENCE is placed with the variable declarations. […]
Jim Dempsey

That's a very positive point!  
Equivalence has a very simple and clear use for developers and the variables will be very clearly identified.
In addition, the associated variables can no longer be changed by mistake in the rest of the developments.
In our developments, all variables are declared and initialized in modules separate from the rest of the code. So there is very little risk of REAL/INTEGER confusion.  


Steve Lionel (Ret.) (Blackbelt)
wrote:
[…] if you EQUIVALENCE an INTEGER and a REAL, and define the INTEGER, the REAL becomes undefined. […]

This means that the compiler or the execution will inform me of the improper use of an undefined variable in case of an error.

Luigi R. wrote:

Not using EQUIVALENCE that has a deep root in the assembler language can prevents you to create efficient software architectures.


The performance of my code is a real priority.
It seems to me that efficiency and performance have more or less the same meaning.
 
My conclusion is that EQUIVALENCE is an excellent solution to my problem. But I'm just a paper belt... so I'm very interested in your explanations!


Thanks thanks thanks :-)

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,580 Views

FYI Being a paper belt has no bearing on your programming ability. Welcome to this forum.

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor II
2,580 Views

jimdempseyatthecove (Blackbelt) wrote:

>>ASSOCIATE can do much more than UNION or EQUIVALENCE...
Correct, however, it cannot retype a variable/array/array section, or re-layout a record, whereas a UNION (or a "cast") can. ..

Jim, what do you mean by "re-layout a record"?

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,580 Views

...

TYPE MyRecord
UNION
  MAP
     REAL(8) :: BLA
  END MAP
  MAP
     CHARACTER(LEN=8) :: NAME
  END MAP
  MAP
     INTEGER(4) :: COUNT
  END MAP
END UNION
... ! other record items
   REAL(8) :: OTHER(nnn)
END TYPE MyRecord

And use that type to read records from a file (as binary).

When the data file contains POD (plain old data), and when you cannot tell what the record is until after you read the record, it is useful to be able to describe the record in a manner that has a multiplicity of declarations. A similar issue when writing arbitrary POD files.

Jim Dempsey

0 Kudos
FortranFan
Honored Contributor II
2,580 Views

Thanks, I presumed you were referring to UNIONs but I wanted to be sure.

Would you by any chance have a fully worked out, real-life example of record processing using this non-standard UNION type along with some measure of performance you get, either in terms of speed and/or data size that you think is important to consider?

Now, one can achieve something similar to the above use case of UNIONs involving POD via polymorphiic types, TRANSFER, etc. in Fortran.  But then the issue brought up often is performance.  But as far as I know, there has been no objective quantification of any difference in performance with UNIONs vs some standard-conforming one, just words and references to inefficient assembler code, etc.  That does not help move beyond the status quo with Fortran.  With an actual example, if someone can illustrate substantial difference in non-standard UNION and what the standard offers now, that could help contemplate what to consider in a future revision to the standard - it's worth an online discussion (see below) among the interested parties. 

Readers: please see this recent site at GitHub: https://github.com/j3-fortran/fortran_proposals

0 Kudos
jimdempseyatthecove
Honored Contributor III
2,299 Views

I can show you parts of an example, but it would be difficult to provide metrics. Description follows:

I have a physics simulation program Space Elevator, tether emulation, body motion, rigid beam object, planetary, etc... Part of the simulation program includes the ability to periodically log data to a database of my design. The database store, and subsequent playback read, has the capability to selectively include, exclude, and type conversion (precision changing). IOW some data is not interested in saving (e.g. transient and temporary) and other data as an example may be adequately converted from REAL(8) to REAL(4). e.g. for the rotation of a body I would want to integrate many steps using REAL(8) but snapshot using REAL(4). Or integer variables may execute fastest in native bit-ness (INTEGER(4)) but are known to be competently stored using a lesser precision (INTEGER(2)).

I also wanted the conversions to be declared dynamically. Meaning on one simulation run, store in low precision and filtered down to minimal data for quick analysis, and later in a different simulation run store in high precision and filtered up to include additional data.

The simulation program will not know the type sizes and/or packing/unpacking filters until run time, or until it opens a prior written database.

Consider the following record which can be used as both a header record with data of a blob (bucket) of data as well as a "stream reader" of the data within the blob following the header portion of the blob.

type TypeAVRDBBlobContext
    ! Align real 8 at front of context
    union
    map
    real(8) :: valREAL8
    end map
    map
    real(4) :: valREAL4
    end map
    map
    integer(8) :: valINT8
    end map
    map
    integer(4) :: valINT4
    end map
    map
    integer(2) :: valINT2
    end map
    map
    integer(1) :: valINT1
    end map
    map
    integer(1) :: ArrayINT1(8)
    end map
    map
    character(8) :: Name
    end map
    end union
    
    ! continue with real
    real(8) :: theMin, theMax, theRange, theMidpoint, theDelta
    
    ! Next integers
    integer :: IOUnit
    integer :: ByteCount
    integer :: Mode         ! current SaveAs mode
    integer :: BlobNumber
    integer :: FrameNumber
    
    ! vvvvv duplicate into header
    integer(4) :: BlobSize   ! number of bytes per record (512, 1024, 2048, ...)
    integer(4) :: BlobNumberFrame1
    integer(4) :: NumberOfBlobsPerFrame
    integer(4) :: NumberOfBlobs
    integer(4) :: NumberOfFrames
    ! ^^^^^ duplicate into header
    
    ! last allocatable buffer for Blob(BlobSize)
    integer(1), pointer :: Blob(:)
end type TypeAVRDBBlobContext

The pointer Blob is messaged on read/write such that the blob data immediately follows this header layout in the database.

To use the data I have get/put functions to, in conjunction with the filter and size conversion information, to read or write data in stream-like manner using one of the types listed in the front of the record. (then advance the pointer to the Blob)

In the simple case of saving a REAL(8), default real specified as real(8)

! routine to save REALs in arbitrary mode
subroutine AVRDB_Save_REAL(SaveAs, value)
    use MOD_AVRDB
    implicit none

    ! input args
    integer(1) :: SaveAs
    real :: value
    ! local variables
    integer :: valueAsINTEGER   ! 
    
    AVRDB%out%bc%Mode = SaveAs
    select case(SaveAs)
    case(NotRecorded)
        return
    case(AsINT1:AsINT8)
        valueAsINTEGER = value
        call AVRDB_Save_INTEGERquick(valueAsINTEGER)
        return
    case(AsREAL4:AsREAL8)
        call AVRDB_Save_REALquick(value)
        return
    case(AsCompressed1REAL4rd:AsCompressed4REAL8d)
        ! compression performed for arrays only
        CALL DOSTOP('MOD_AVRDBcode - invalid AVRDB_Save_Mode')
        return
    case default
        CALL DOSTOP('MOD_AVRDBcode - unassigned AVRDB_Save_Mode')
    end select
end subroutine AVRDB_Save_REAL

And for the case of ASREAL4:ASREAL8

subroutine AVRDB_Save_REALquick(val)
    use MOD_AVRDB
    implicit none

    real :: val
    ! local variables

    ! code 

    
    select case(AVRDB%out%bc%Mode)
    case(AsREAL4)
        AVRDB%out%bc%valREAL4 = val
        if((AVRDB%out%bc%ByteCount+4) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc)
        AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+4) = AVRDB%out%bc%ArrayINT1(1:4)
        AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 4
        return
    case(AsREAL8)
        AVRDB%out%bc%valREAL8 = val
        if((AVRDB%out%bc%ByteCount+8) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc)
        AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+8) = AVRDB%out%bc%ArrayINT1(1:8)
        AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 8
        return
    case(AsCompressed1REAL4rd,AsCompressed1REAL4d,AsCompressed1REAL8rd,AsCompressed1REAL8d)
        if(AVRDB%out%bc%theDelta .ne. 0.0) then
            AVRDB%out%bc%valINT1 = INT1((val-AVRDB%out%bc%theMidpoint) / AVRDB%out%bc%theDelta)
        else
            AVRDB%out%bc%valINT1 = 0
        endif
        if((AVRDB%out%bc%ByteCount+1) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc)
        AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount) = AVRDB%out%bc%ArrayINT1(1)
        AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 1
        return
    case(AsCompressed2REAL4rd,AsCompressed2REAL4d,AsCompressed2REAL8rd,AsCompressed2REAL8d)
        if(AVRDB%out%bc%theDelta .ne. 0.0) then
            AVRDB%out%bc%valINT2 = INT2((val-AVRDB%out%bc%theMidpoint) / AVRDB%out%bc%theDelta)
        else
            AVRDB%out%bc%valINT2 = 0
        endif
        if((AVRDB%out%bc%ByteCount+2) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc)
        AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+2) = AVRDB%out%bc%ArrayINT1(1:2)
        AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 2
        return
    case(AsCompressed4REAL4rd,AsCompressed4REAL4d,AsCompressed4REAL8rd,AsCompressed4REAL8d)
        if(AVRDB%out%bc%theDelta .ne. 0.0) then
            AVRDB%out%bc%valINT4 = INT4((val-AVRDB%out%bc%theMidpoint) / AVRDB%out%bc%theDelta)
        else
            AVRDB%out%bc%valINT4 = 0
        endif
        if((AVRDB%out%bc%ByteCount+4) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc)
        AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+4) = AVRDB%out%bc%ArrayINT1(1:4)
        AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 4
        return
    case default
        CALL DOSTOP('MOD_AVRDBcode - unassigned AVRDB_Save_Mode')
    end select
end subroutine AVRDB_Save_REALquick

in the case of ASREAL4, the value val is converted from real (real(8)) to real(4) in the process of being written into header union valREAL4. Then using the UNION's ArrayINT1(8), 4 bytes are copied into the integer(1) array Blob beginning at index ByteCount+1. Then the ByteCount is advanced.

With the UNION, all this hoop jumping, is simple table driven. Without UNION I would not have this flexability.

Jim Dempsey

0 Kudos
Reply