7

In python (using numpy), I can broadcast an array to a different shape:

>>> import numpy as np
>>> a = np.array([2,3,4])
>>> b = np.zeros((3,2))
>>> b[:,:] = np.zeros((3,2))
>>> b[:,:] = a[:,np.newaxis]  #<-- np.newaxis allows `a` to be "broadcasted" to the same shape as b.
>>> b
array([[ 2.,  2.],
       [ 3.,  3.],
       [ 4.,  4.]])
>>> c = np.zeros((2,3))
>>> c[:,:] = a[np.newaxis,:]
>>> c
array([[ 2.,  3.,  4.],
       [ 2.,  3.,  4.]])

Is there any way to achieve the same effect in fortran? I have a subroutine which expects a 2D array to be passed in -- I would like to "broadcast" my 1-D arrays up to 2-D as I've demonstrated above. As it seems that it is likely to matter, my 2D array does have an explicit interface.

As a side note, I thought that this functionality might be provided by the reshape intrinsic, -- Something like:

real,dimension(3) :: arr1d
reshape(arr1d, (/3,3/), order=(/1,/1))

but after reading the docs, I don't think that this is possible since order seems to need to include all the numbers 1 to "N".

Edit: To be a little more clear, I'm looking for a simply way to create a couple of transforms on an input a such that:

case 1

b(i,j) .eq. a(i)  !for all j, or even just j=1,2

and

case 2

b(j,i) .eq. a(i)  !for all j, or even just j=1,2

bonus points1 for arbitrary dimensionality:

b(i,j,k) .eq. a(i,j)
b(i,k,j) .eq. a(i,j)

etc.

1disclaimer -- I don't actually have SO super powers to bestow bonus points upon the answerer ;-)

ali_m
  • 71,714
  • 23
  • 223
  • 298
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • @HighPerformanceMark -- That's a good point. Maybe I will. (I hadn't thought of it because I've never done it before) – mgilson Feb 04 '13 at 15:01

2 Answers2

6

I'm not sure what you are trying to accomplish but here are a couple of fragments which may help.

reshape can take an optional argument, called pad, which can be used to provide the 'extra' elements needed when you reshape into an array with more elements than you started with, say from 3x4 to 2x4x2.

You may also be interested in the spread function which is designed for 'upranking' arrays, that is taking a rank-N array in and putting out a rank-N+1 array. The fragment in your second copy could be rewritten as

array2d = spread(array1d,2,2)

In this example the second argument is the dimension along which to spread the first argument to make the output. The third argument is the number of copies of the input array to make.

PS The call to spread should perhaps be spread(array1d,1,2), I haven't checked it.

EDIT in response to OP's editing of question

The two cases, 1 and 2, are satisfied by spreading across dimensions 2 and 1 respectively. In Fortran

b = spread(a,2,j)

and

b = spread(a,1,j)

Since spread returns an array with rank 1 greater than the rank of its first argument, it provides the sought-for arbitrary dimensionality. However, since it's so space-consuming to show arrays of rank-3 and above I'm not going to.

High Performance Mark
  • 77,191
  • 7
  • 105
  • 161
  • I'm checking out your answer now (sorry I didn't have a chance to check it out over the weekend). It looks like `spread(array1d,1,2)` satisfies case2 in my updated question, but I can't figure out how to get it to satisfy case1 (or any of the bonus cases ;-). – mgilson Feb 04 '13 at 15:02
  • Post that bounty and I'll have another look :-) – High Performance Mark Feb 04 '13 at 15:03
  • I can't for 12 hours -- So the next time I'm here when the question is eligible for the bounty, I might post one. – mgilson Feb 04 '13 at 15:06
  • I see you addressed my edit without the bounty -- I've added a small one anyway for fun :). I'm still curious as to whether there is a better way to do it, especially for the rank > 2 case -- e.g. somehow making rank N slices of a rank N+1 pointer array point to the original array within a user defined function. doing it without the pointers seems like it wouldn't be too difficult... – mgilson Feb 05 '13 at 13:48
1

The reshape intrinsic will allow you to copy the 1D array to a 2D array. With a sufficiently recent Fortran compiler their is a pointer technique. A pointer provides a second way of referring to the storage, avoiding the copy. The method is "pointer bounds remapping". An example:

program array_tst

  integer, dimension (4), target :: array_1d
  integer, dimension (:,:), pointer :: array_2d

  array_1d = [ 1, 2, 3, 4 ]

  array_2d (1:2, 1:2) => array_1d

  write (*, *) array_2d (1,1), array_2d (1,2), array_2d (2,1), array_2d (2,2)

end program array_tst

Also see changing array dimensions in fortran

P.S. In reply to the comments ... if you don't mind copying the array, here is how to use reshape:

program array_reshape

  integer, dimension (4) :: array_1d
  integer, dimension (2, 2) :: array_2d

  array_1d = [ 1, 2, 3, 4 ]

  array_2d = reshape ( array_1d, [2,2] )

  write (*, *) array_2d (1,1), array_2d (1,2), array_2d (2,1), array_2d (2,2)

end program array_reshape
Community
  • 1
  • 1
M. S. B.
  • 28,968
  • 2
  • 46
  • 73
  • wait -- I'm confused -- How does this allow me to use `reshape` to do what I proposed? I'm actually not worried about the copy here ... if that makes a difference. – mgilson Feb 03 '13 at 03:14
  • And, as a side note, it's not yet implemented with my version of `gfortran` -- I don't think that our code (which I'm just now updating to fortran90!) is ready for something that bleeding edge ;-) – mgilson Feb 03 '13 at 03:16
  • I think you mis-understand. With an input array length 4, I'm not looking to get something that is shaped (2,2). I'm looking to get something shaped (4,2) -- or (2,4) -- or (N,4). I want to get both scenarios. It could be accomplished: `do i=1,2; array_2d(i,:) = array_1d; enddo` for example. – mgilson Feb 03 '13 at 03:21