-
Notifications
You must be signed in to change notification settings - Fork 168
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
diag, eye, and trace #169
Comments
Thank you! I like this. I have one comment: I think the function should not return an allocatable --- I think it could be faster to simply return the array and expect the caller to provide a correctly pre-allocated array as a result. |
Yes, your comment is related to the discussion in #114. If the LHS is also allocatable (and unallocated), then I think the compilers will do the right thing and not create a temporary array. Would there be any interest in having two versions, say |
Thanks for finding the prior discussion. What is the advantage of having it allocatable result? With auto allocate LHS, wouldn't the compiler properly allocate the LHS anyway? |
Yes, it does. ...
real, allocatable :: x(:, :)
allocate(x, source = diag(x)) |
Thank you for this.
I would suggest to support all types, as for the other functions. Would a subroutine not easier to implement and to use for this scenario? E.g., call eye(x)
I would say yes, while I will probably never use it myself.
Yes, as you proposed should be fine. I guess that a negative number could be provided to extract elements below the diagonal?
I am not sure to understand the question. I think you could loop until
I would answer yes, while I would never use it myself. |
The downside of a subroutine is then you cannot chain the identity matrix into expressions, e.g. real :: M(9,9), J(9,9)
M = eye(9) + 1./(tau + 0.5)*(eye(9) - J)) On the other hand as a subroutine it would encourage reuse real :: M(9,9), J(9,9), identity(9,9)
call eye(identity)
M = identity + 1./(tau + 0.5)*(identity - J)) Then again, I see this only as a convenience function, and not to be used inside some expensive kernel, as it is clear there are more efficient ways to add values to the diagonal of a matrix. @jvdp1 Do you have any suggestion how to control the return type of
From the numpy API for diag: I noted now the Julia ˙Diagonal` is a bit different. Given a matrix A, it returns a matrix keeping only the elements on the diagonal (and setting the other values to zero)
Your suggestion is equivalent to what I have now: function diag_rsp_mat(A) result(res)
real(sp), intent(in) :: A(:,:)
real(sp) :: res(minval(shape(A)))
integer :: i
do i = 1, minval(shape(A))
res(i) = A(i,i)
end do
end function diag_rsp_mat Until some form of assertion mechanism is available, I think this is the easiest solution. For the |
Would a stragegy like the one used for real(sp) :: a
real(sp), allocatable :: b(:,:)
b= eye(a,5)
b= eye(1._sp, 5) |
Thanks for this proposal. I like it. Some comments:
Specifically regarding
Why different return types? It's just a bunch of ones and zeros of some desired shape. In the same vain, why even double precision result? Why not just integer? You can then use it directly in expressions and let the compiler promote it to the higher types or kinds if needed. Integer-only result for |
Thank you both for your comments. @jvdp1 Yes, that would be the option giving full control to the caller. @milancurcic I like your solution with implicit type promotion as it simplifies the code. If most of you agree with the implicit type promotion solution, then integer function eye(n) result(res)
integer, intent(in) :: n
integer :: res(n,n)
integer :: i
res = 0
do i = 1, n
res(i,i) = 1
end do
end function and can be used as to fill both fixed-size or allocatable arrays, and also appear in expressions: real(sp) :: a(0:5,0:5)
real(dp), allocatable :: b(:,:)
integer(int32), allocatable :: t(:,:)
a = eye(6)
b = eye(9)
t = 3*eye(12)
call random_number(a)
a = a + eye(6) This works fine with the ifort compiler and allocate(b,source=real(eye(9))) |
What do you mean by "implicit type promotion"? I didn't get that part. I agree it should look like this: function eye(n) result(res)
integer, intent(in) :: n
integer :: res(n,n)
integer :: i
res = 0
do i = 1, n
res(i,i) = 1
end do
end function (You had an extra |
Oops, thanks. What I meant was we have only one copy of |
I see. I think that's fine. |
@ivan-pi I am ok with the implicit type promotion, as proposed. |
I think this is a spurious warning. I strongly recommend against putting workarounds in code only to suppress warnings. In this specific case, I don't think that the compiler is warning about anything reasonable, as far as I can tell.
Is |
I've gone forward and made a draft pull request (#170), implementing most of your suggestions. 👍 |
@ivan-pi thank you for doing this! Looks very nice. |
We can now use Would it make sense to also be able to assign to a diagonal of an existing matrix? |
I guess so. In Octave, I usually do that as: D=diag(diag(A)) |
Wouldn't a subroutine be more suitable for this? Something along the lines of subroutine set_diag(A,v,k)
real, intent(inout) :: A(:,:)
real, intent(in) :: v(minval(shape(A)) - abs(k))
integer, intent(in) :: k
end subroutine |
I remembered there was a discussion on the topic of setting the diagonal over at j3-fortran/fortran_proposals#14 There was also a discussion for the identity matrix j3-fortran/fortran_proposals#103 with this interesting Fortran one-liner: integer, parameter :: n = 4 ! dimension of the identity matrix
real(wp), parameter :: I(n,n) = RESHAPE([ (MERGE(1._wp, 0._wp, i/n==MOD(i,n)), i=0, n**2-1) ], [n,n]) |
Another way would be to have a function which returns the diagonal indices, like in numpy:
This could work nicely, if it was possible to slice into a rank-2 array as a rank-1 array. Or perhaps a solution using the intrinsic real :: A(3,3)
A = 0
A = unpack([1.,2.,3.],eye(3) == 1, A) Hopefully a smart compiler would access only the values of A where eye(3) == 1 equals .true. |
Hooray! With pull request #170 we have a new module for linear algebra. I've edited the labels to reflect the new status. |
Related to #10
Convenience functions for use in linear algebra. The functions would go into
stdlib_experimental_linalg.f90
. Simple implementation by looping over the diagonal. Type-generic (integer, real, complex) implementation using the fypp preprocessor.A draft is available here: https://github.com/ivan-pi/stdlib/blob/ivan-pi/src/stdlib_experimental_linalg.fypp (based on the version from fortran-utils #103 )
eye
Returns the identity matrix
eye(n [, kind])
)? Or just default to double precision?identity
, ... ?trace
Returns the sum of diagonal elements
axis
argument?diag
Create a diagonal matrix from a vector or extract a diagonal from a matrix
diagonal
, ...Other languages
The API's differ slightly between languages, some are more flexible than others.
The text was updated successfully, but these errors were encountered: