LFortran now supports all intrinsic functions

We are very happy to announce that LFortran now supports all intrinsic functions.

Blog post: LFortran supports all intrinsic functions -

X: x.com

Big thanks to @certik and all the contributors for their hard work!

31 Likes

Thank you Harshita, @Parth1211, @Pranavchiku and the rest of the team! Great job on this. It was a big effort and I am very happy how it turned out, we have a very solid implementation and design of all intrinsics, see the blog post for all the details.

6 Likes

Fortran array intrinsic functions such as sum are very flexible, working on arrays of any rank, with a dim argument allowing a sum along a specified axis, and a mask argument specifying which elements to sum over.

A user can write array reduction functions such as mean or sumsq (sum of squares) and use an interface with module procedures such to overload them to various ranks, but this is tedious. One can write sum(x**2) instead of sumsq(x), but maybe it is faster to access the elements of x once instead of first squaring the elements and then calling sum.

Would it be possible for LFortran to define extra array intrinsics, perhaps in a module lfortran_intrinsics_mod? Given a C or Fortran function that handles the 1D case of an array reduction function without a mask, it would be nice if the machinery LFortran has to implement sum could be applied to other functions, allowing them to have array arguments of any rank and to have a mask argument.

3 Likes

There are several array intrinsics in MATLAB that I would love to see in Fortran (and am puzzled why they haven’t already been implemented). An example would be the set operations. I’ve written my own work-alikes for several of these functions to help define the support for least-square operators in finite volume CFD codes. It would be great to have these as intrinsic functions. I’m sure others can identify several other MATLAB functions they would like to see implemented. Maybe this is something the community could do as part of stdlib.

was made just to look like a subset of Matlab set functions. They were originally a one-off for a specific project but others have used them since with no reports of issues. They are available via fpm(1) as a project file. So far, I do not know that they have been used at large scale so not sure how they perform as-is against Matlab if used in large CFD problems but there is plenty of room to speed them up. Do you have a simple benchmark in Matlab that can be used for comparison? I do not currently have access to Matlab.

Nice progress!

Such functions (e.g., mean, variance, …) are implemented in the Fortran stdlib, and could be a good starting point IMO.

3 Likes

My uses are primarily sets of around 16 to 100 integers. Mostly I’m using functions like union, unique and intersect to filter out repeated node and cell indicies. My code supports the following. unique, union, intersect, setdiff, ismember, setxor. I use an optional tolerance argument for floating point functions to avoid the additional functions MATLAB has for that purpose. For testing I just used the examples on the MATLAB site. I no longer have access to MATLAB but I’ll see if Octave supports the set functions. I’ve not benchmark for speed but I doubt that the Fortran code would be slower than the MATLAB code.
See the MATLAB link above for the examples. If you want I’ll package up my code and post it on Github. It will take me a few days to get everything in one package and get it to work with fpm

1 Like

@Beliavsky we can. Can you write an example? Are you thinking of using some new attribute or pragma?

I was concerned about performance if called with large arrays and/or millions and up invocations so that should not be a problem. It is fine as-is although any additional versions packaged as fpm packages is always welcome from my perspective.

I am asking if given a C++ function that takes an array argument and length and returns a value of the same type of the array, for example sumsq below, can LFortran generalize it to various ranks and to have an optional mask, so that the user has access to a sumsq function with the flexibility of the intrinsic sum?

#include <stddef.h> // For size_t

double sumsq(const double *array, size_t length) {
	if (length == 0) {
		return 0.0;
	}
	double total = 0.0;
	for (size_t i = 0; i < length; i++) {
		total += array[i] * array[i];
	}
	return total;
}
1 Like

@Beliavsky we can implement it, but I would like to see more details how it can work.

So you write sumsq in Fortran:

function sumsq(array, length) result(total)
integer, intent(in) :: length
real(dp), intent(in) :: array(length)
real(dp) :: total
integer :: i
total = 0
do i = 1, length
    total = total + array(i) * array(i)
end do
end function sumsq

Now how do you tell the compiler to use it like the intrinsic sum with the dim attribute and any rank?

Some options are a pragma !$lf anyrank, or anyrank attribute. How would the compiler figure out how to insert the dim argument structure in the loop? Are you thinking that it should “pattern match” a single loop inside the function?

In this case it’s a reduction, so maybe using a do concurrent with reduce we can make the compiler to automatically make it work for any rank and dim structure.

If you can write a full example of code that you are hoping to compile, with the function declaration and usage and a pragma or a language extension, that would be helpful. We should propose it on the proposals repository as well.

I think programmers would also expect an interface like

real(dp), intent(in) :: array(:)

to allow the actual argument to be an array slice of some kind. For functions like this where each element is referenced a single time, a copy-in(/copy-out) kind of argument association would basically double (or even 3x or 4x) the overall memory references. This is, after all, the same kind of unnecessary memory overhead that these functions are trying to avoid when written as, for example, sum(x*x). There is nothing in the language that prevents the expression sum(x*x) from being evaluated with a single memory reference for each array element, but this is not at present under direct control of the programmer.

With the present language features, the programmer would need to write separate functions for the various ranks, and include all of them within a generic interface. Another approach would be to use an assumed rank declaration for the dummy argument, and inside the routine a select rank construct would be used to separate out the various rank code blocks. So that part is at least possible now within the language. Another useful feature would be for the compiler to recognize the argument rank at compile time, and to somehow branch directly to that code within the select rank construct, avoiding any runtime branching overhead in the same way that the generic interface approach avoids that overhead. Perhaps the proposed generic features will streamline this process?