-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Matrix literals #102
Comments
Yes! I've been wishing something like this for years. |
Agreed. The |
Not sure about this. Otherwise I agree with the usefulness of matrix literals. |
Sure, for performance you need to know that. But for showing a beginner how to declare a 3x3 matrix parameter it's not really that great. |
Indeed. You need to know the memory layout to write performant code, but it should not be necessary to know the layout just to write correct code (even if slow).
…On Thu, Nov 28, 2019, at 8:57 AM, Jacob Williams wrote:
Sure, for performance you need to know that. But for showing a beginner
how to declare a 3x3 matrix parameter it's not really that great.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#102?email_source=notifications&email_token=AAAFAWGUE4ITA6HAVQQQ4VLQV7S7ZA5CNFSM4JSOR54KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFM7NNA#issuecomment-559543988>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAAFAWBGWAJ7YPPOS3JGPDLQV7S7ZANCNFSM4JSOR54A>.
|
Agreed, I'd interpreted your sentiment as a more general desire to completely abstract away storage order to compiler implementation. |
I understand that matrix literals are for defining small matrices anyway. I
agree that better syntax is needed.
czw., 28 lis 2019, 17:14 użytkownik LKedward <[email protected]>
napisał:
… Sure, for performance you need to know that. But for showing a beginner
how to declare a 3x3 matrix parameter it's not really that great.
Agreed, I'd interpreted your sentiment as a more general desire to
completely abstract away storage order to compiler implementation.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#102?email_source=notifications&email_token=AC4NA3MT33YGMME4BXXBX63QV7U6JA5CNFSM4JSOR54KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFNAVJY#issuecomment-559549095>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AC4NA3PLH3FJE5AIC7H7K3DQV7U6JANCNFSM4JSOR54A>
.
|
With these things being said, and returning to the last point in the original post, there is an argument for using row-major in the literal syntax in that it is the same as MATLAB and therefore will be familiar to beginners coming from MATLAB. |
MATLAB also accepts commas as column delimiters instead of spaces and this is generally recommended for readability. i.e. The following are equivalent in MATLAB a = [1 2 3; 4 5 6; 7 8 9]; a = [1, 2, 3; 4, 5, 6; 7, 8, 9]; It is the same comma/semicolon syntax as suggested in the original post. An alternative syntax not using the semicolon could be nested array literals: integer :: a(3,3)
a = [[1,2,3],[4,5,6],[7,8,9]] but this again raises the row-major/column-major syntax question. |
I certainly think that the limitations of fixed-form source should not enter the deliberations. It's long past time for that to be eliminated (#108). |
After spending several days recently typing in data to create equation of state tables, I can say from experience that anything like the MATLAB way of defining multi-dimensional arrays would be a big improvement over RESHAPE. I've never understood why you need RESHAPE if you are entering data in column major order in the first place. |
I could certainly see relaxing the shape matching rules if the right side was a rank-1 array with the same SIZE as the left. I do wonder how these proposals would extend to rank 3 and greater arrays. |
An existing alternative I haven’t seen mentioned here is using a integer :: A(2,2)
data A /1,2, 3,4/ This isn’t as nice as the proposed syntax, and there’s still the row/column ordering issue, but I tend to find it slightly more readable than |
@zjibben wrote:
I've avoided DATA statements in any code I wrote and I've shied away from ever recommending DATA statements because it attaches the perilous "implied SAVE" attribute to objects in Fortran: From the standard:
|
@FortranFan That is a very good point, and probably why I’ve only seen it used for constant module-level data.. |
Some initial thoughts. If: integer,save :: xx(3,5)= reshape([&
|
That sounds to me like a very pragmatic solution! Since we can let multi-rank pointers point to rank-1 targets, one could similarly allow the assignment of a rank-1 array to a multidimensional array, provided, the shape of the LHS is known (so not automatic reallocation occurs). We could then write:
I dont't think, that the other proposed forms (introducing new symbols) are much more readable than those examples. Actually, using Fortran's array-merging strategy one could even write:
which is on par with any other notations suggested so far. You would still have to remember, that Fortran has column-major ordering (so lines in the literal correspond to 'columns' in the actual array), but that is IMO acceptable. |
I like this syntax the most: integer :: a(3,3)
a = [[1,2,3], [4,5,6], [7,8,9]]
print *, a That's how Python does it. As @septcolor mentioned in #102 (comment), currently Fortran flattens such arrays. What is the motivation for this "flattening"? I think rethinking this and allowing the natural |
@certik If the assignment of rank-1 arrays to rank-N arrays were allowed, your example would work immediately without the need for any backwards incompatible changes. As long as the shape of the LHS is unambigous, the flattening of the literal of the RHS won't be an issue. We would only have troubles with automatic reallocation, such as
as here it is impossible to find out, how RHS should be reshaped to match LHS. For those cases, the compiler would have to emit an error and stop. |
I see. I still think removing this automatic "flattening" would help. What is the motivation for it? We can introduce a function a = flatten([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) But otherwise it should not flatten by default in my opinion. |
Well, removing the flattening would be backwards incompatible, as the following code snippet won't work any more:
|
True. But this would work: program test
implicit none
integer, allocatable :: a(:), b(:)
a = [1, 2]
b = flatten([a, a])
print *, b
end program test I agree since this is backwards incompatible, we might not want to do that. But if the only motivation for automatic flattening is backwards compatibility, then it would seem this was a bad design decision long time ago. |
Unfortunately, it is not just flattening, but also automatic merging. The assignment
currently works (provided I agree, that the current syntax is weird and I have no clue, why it was introduced. But I don't think we have any chance to delete it from the language. So, the best compromise IMO is still the assignment of rank-1 arrays to rank-N ones without the need for a reshape, as suggested by @sblionel . |
Given that I used this rather recently, that chance is indeed rather slim. It's convenient to concatenate arrays, although an intrinsic function would have been the better choice back when that syntax was introduced. I'm still keen on doing something similar to Matlab or Julia as in my initial proposal (I'm the original comp.lang.fortran poster), but I haven't yet found a better alternative. I'd be weary of introducing anything that doesn't also scale to rank 3 (or higher) arrays, though. |
@elecprog thanks for the feedback and for the proposal. I think there is still an opportunity here to do something, but we have to figure out how to do it to satisfy all the requirements/issues stated in this thread. |
When I think of all the capabilities available in READ() including list-directed and implied DO constructs if there were some equivalent of a "here" document in Fortran it woud allow for inputting small arrays but also text blocks without having to use continuation characters and quotes as well.
Almost no new syntax, useful for small arrays but maybe more so for text; would be really nice to have some kind of block comment too, maybe just
|
Maybe we could take some inspiration from the syntax introduced in Julia v1.7 (see NEWS.md). A first thing to note in Julia's case, A Julia does not allow mixing of commas and semicolons, but it does allow spaces and semicolons, giving for example: Which is also another motivation of the automatic flattening as this is also how it's written in mathematics (using newlines for concatenation in the column direction instead of semicolons 😉) Fortran already has a concatenation operator |
The problem is quite simple if one simply wants an intuitive way to write matrices (aka rank 2 arrays). The semicolumn
A general mechanism to concatenate arrays of arbitrary ranks and along arbitrary dimensions is something that goes far beyond the initial motivation of the proposal... Such a mechanism can hardly be simple and fully intuitive on the one hand, and we generally don't need to intuively write rank 3 (or more) arrays like we do for a matrix. |
I can live with @PierUUs solution. I am still curious as to why the standard requires you to use RESHAPE instead of just assuming that you want to map the data in the rank 1 initializer to the the column major order of the target array. ie. `` ! should just map to |
I tend to disagree here: Fortran is traditionnally THE langage for HPC, and performance should be at the core of it. And the sooner the beginners learn about memory layout and its consequences, the better. It's actually not only about performances (after all, on some hardware the memory layout doesn't matter), it also helps understanding some behaviours (e.g. how a rank 2 array can be associated to a rank 1 array...) |
A much. much, much longer "tradition" with Fortran is "formula translation" for the programmer and for the processor to then adopt to it in a performant manner. Let us not place the cart before the horse here. |
What I essentially say is that the cart and the horse should be placed at the same time. The by far best programming course I got, a long time ago, did not teach me any language syntax, but how a computer was computing under the hood, "when you do that in your high level langage, here is what really happens in the chips" |
I also agree. At some point we got the flexibility in the language to have a rank-N pointer pointing to a rank-1 array, so have the same kind of flexibility to assign a rank-1 array to a rank-N array looks reasonnable (the downside is that no more error-checking would be possible for such an assignement by mistake). This doesn't solve the "by row" notation, though, and
Would make sense in addition. For those who think that a notation that doesn't gracefully generalize to any arbitratry rank is not wishable, one could think about a notation that would make clear that this is an exception for matrices. For instance:
The
|
Note, that if you are OK with column-wise notation (instead of row-wise notation), the change suggested by @rweed is enough and would provide a solution for arbitrary ranks, as Fortran automatically flats embedded arrays into a 1D array:
|
To me, a column-wise notation is almost pointless: if one wants a notation that is intuitive, it has really to mimic how matrices are usually written, i.e. row-wise (and the original post of this discussion is all about row-wise, btw).
But as discussed above, this would not be backward compatible with the current standard. |
The notion someone may carry Fortran "is traditionnally THE langage for HPC" or how one was taught is not at all pertinent to the discussion in this thread as to the easier options the language can offer with writing out matrix literals. The language currently offers ..
.. = reshape( [ .. xx, yy, zz, .. ], shape=.. )
! or
.. = reshape( [ .. xx, yy, zz, .. ], shape=.. , order=[..] )
. Having easier, literal ways to write out the matrix than having to use |
My initial idea for handling row-wise vs column wise would be to add an attribute for arrays of intrinsic type that specifies row-wise storage. Something like integer, storage = "ROW_WISE" :: a(2,3) = [1,2,3,4,5,6] Unfortunately on further thought I can see this causing a lot of issues with optimization, vectorization etc and would force the compiler developers to make major changes to their existing array handling code. To bad because on the surface I think (from a syntax standpoint at least) being able to specify storage order might be a simple way to eliminate (or at least reduce) the need for RESHAPE and or TRANSPOSE for defining arrays. |
Probably this syntax is more natural: Why couldn't
always mean row major, even if internally the array is stored as column major? This is all compile time, so the compiler can rearrange things appropriately to be efficient. There are backwards compatibility issues, but I still think the above would be the most natural and Fortran almost can do it. It would be very nice if it could do it completely. |
Where did I write it wouldn't? |
Note the ifort compiler appears to implement allowing a vector on the LHS to fill an array on the RHS of any defined shape program testit
logical, parameter :: T=.true., F=.false.
!integer :: ii(3, 2) = reshape([(i, i=1, size(ii))],shape=shape(ii))
integer :: ii(3, 2) = [(i, i=1, size(ii))]
write(*,*)rank(ii),shape(ii)
call printi('ii=', ii)
call printi('[ii]=', [ii]) ! flattens it
call printi('pack(ii,T)=', pack(ii,T)) ! flattens it
contains
subroutine printi(title,a)
implicit none
!@(#) print small 2d integer scalar, vector, matrix in row-column format
character(len=*),intent(in) :: title
integer,intent(in) :: a(..)
character(len=*),parameter :: all='(" ",*(g0,1x))'
character(len=20) :: row
integer,allocatable :: b(:,:)
integer :: i
write(*,all,advance='no')trim(title)
! copy everything to a matrix to keep code simple
select rank(a)
rank (0); write(*,'(a)')' (a scalar)'; b=reshape([a],[1,1])
rank (1); write(*,'(a)')' (a vector)'; b=reshape(a,[size(a),1])
rank (2); write(*,'(a)')' (a matrix)'; b=a
rank default; stop '*printi* unexpected rank'
end select
! find how many characters to use for integers
write(row,'(i0)')ceiling(log10(real(maxval(abs(b)))))+2
! use this format to write a row
row='(" > [",*(i'//trim(row)//':,","))'
do i=1,size(b,dim=1)
write(*,fmt=row,advance='no')b(i,:)
write(*,'(" ]")')
enddo
write(*,all) '>shape=',shape(a),',rank=',rank(a),',size=',size(a)
write(*,*)
end subroutine printi
end program testit |
The right-hand side of your sample assignment statement already has a conforming interpretation, so that syntax is not an option. |
Unfortunately the syntax is already taken, so this is indeed not an option without breaking compatibility. :( |
Therefore, as already mentioned above, this could do it:
{EDIT2: as ";" is also the comand-separator, this could make source-code parsing more difficult.} or
as "/" stands for "new line" in format descriptors, {EDIT1: this conflicts with division} or
{EDIT3: or
to make rank-separation more clear } or with any other "meaningful" character {EDIT4: e.g. "#", "^", "$", ... if there is no other possible conflict.} |
The slash / syntax also has a current interpretation open main.f90 in Fortran Playground program main
implicit none
write(*,*) [ [ [1000, 2000] / [3, 4] ] / [ [500, 600] / [7, 8] ] ]
end program main
|
As proposed by a reader at comp.lang.fortran: https://groups.google.com/d/msg/comp.lang.fortran/m6qz7hC4a7M/78maOb9uEgAJ
(see the thread for further comments and discussion)
The text was updated successfully, but these errors were encountered: