Skip to content
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

enh: support compiletime_broadcast_elemental_intrinsic for multiple arguments #3684

Merged
merged 8 commits into from
Mar 23, 2024

Conversation

gxyd
Copy link
Contributor

@gxyd gxyd commented Mar 19, 2024

fixes #3673

for (auto &a : args) {
if (allow_nullptr && !a) continue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this change --- when can an argument here in the vector be null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For something like: floor(1.2) where the second optional argument is set to nullptr.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Let's get all tests passing, then I'll review one more time.

@gxyd gxyd force-pushed the broadcast_intrinsic_functions branch from eb6ff65 to d8faf29 Compare March 20, 2024 10:15
@gxyd
Copy link
Contributor Author

gxyd commented Mar 20, 2024

@certik , could you help me understand if the failing CI jobs are related to my changes?

the error says:

std::allocator<unsigned char> >, void>, bool)':
2024-03-20T10:25:01.8765884Z /home/runner/work/lfortran/lfortran/lfortran-0.33.1-866-ged79bd5c3/src/lfortran/fortran_kernel.cpp:346: undefined reference to `xeus::xinterpreter::publish_execution_result(int, nlohmann::json_abi_v3_11_3::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>,
 std::allocator<char> >, bool, long, unsigned long, double, std::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::vector<unsigned char, std::allocator<unsigned char> >, void>, nlohmann::json_abi_v3_11_3::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long, unsigned long, 
double, std::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::vector<unsigned char, std::allocator<unsigned char> >, void>)'

Not able to understand how to go about this.

while the failing documentation job gives the error:

/usr/bin/ld: /home/runner/work/lfortran/lfortran/src/lfortran/fortran_kernel.cpp:511: undefined reference to `xeus::xkernel::xkernel(xeus::xconfiguration const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::unique_ptr<xeus::xcontext, std::default_delete<xeus::xcontext> >, 
std::unique_ptr<xeus::xinterpreter, std::default_delete<xeus::xinterpreter> >, std::unique_ptr<xeus::xserver, std::default_delete<xeus::xserver> > (*)(xeus::xcontext&, xeus::xconfiguration const&,
 nlohmann::json_abi_v3_11_3::detail::error_handler_t), std::unique_ptr<xeus::xhistory_manager, std::default_delete<xeus::xhistory_manager> >, std::unique_ptr<xeus::xlogger, std::default_delete<xeus::xlogger> >, std::unique_ptr<xeus::xdebugger, std::default_delete<xeus::xdebugger> > (*)(xeus::xcontext&, xeus::xconfiguration
 const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, nlohmann::json_abi_v3_11_3::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long, unsigned long, double, std::allocator, nlohmann::json_abi_v3_11_3::adl_serializer,
 std::vector<unsigned char, std::allocator<unsigned char> >, void> const&), nlohmann::json_abi_v3_11_3::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long, unsigned long, double, std::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::vector<unsigned char, std::allocator<unsigned char> >, void>, nlohmann::json_abi_v3_11_3::detail::error_handler_t)'

@certik
Copy link
Contributor

certik commented Mar 20, 2024

It doesn't seem like triggered by your PR, but something is wrong, it's the same error on all 3 platforms.

Can you try submitting a simple PR to check if master passes?

@certik
Copy link
Contributor

certik commented Mar 20, 2024

The CI failure is unrelated: #3687.

@gxyd gxyd force-pushed the broadcast_intrinsic_functions branch 3 times, most recently from c1e0b6a to 4772a8a Compare March 21, 2024 07:13
@gxyd
Copy link
Contributor Author

gxyd commented Mar 21, 2024

This is ready for a review.

// 2. or arg.size() is 2 and first arg is an array, and second arg "kind" is
// distributed to each broadcasted call
//
if ((args.size() == 2 || args.size() == 1) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think we need this now, we shall go ahead and modify signature to

- void compiletime_broadcast_elemental_intrinsic(ASR::ArrayConstant_t* array, ASRUtils::create_intrinsic_function create_func, const Location& loc, Allocator& al)
+ void compiletime_broadcast_elemental_intrinsic(ASR::expr_t** args, ASRUtils::create_intrinsic_function  const Location& loc, Allocator& al)

Now iterate over args, and store in std::vector<int> array_arg_indices. Once we populate array_arg_indices, for example it has {1, 2, 4} and index {0, 3, 5} are scalar. We will create a helper funciton that takes array_arg_indices and all other necessary parameters and broadcast X(s0, a1, a2, s3, a4, s5 as [X(s0, a1[0], a2[0], s3, a4[0], s5), ...., ].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current approach handles only kind parameter and limits us to only two arguments ( same as main with 1 argument ).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please give an example of an intrinsic function where broadcasting in this way would be needed? i.e. some being arrays, others being scalars, and it's an elemental intrinsic procedure? I'm just not able to think of something.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also have multiple array arguments

program main
print *, max([1.0, 2.0, 3.0], [4.0, 5.0, 6.0])
end program

for (auto &a : args) {
if (ignore_null && !a) continue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need this because in intrinsic_functions.h it is already taken care that there will not be any nullptr. For frontend this happens because we fill_optional_kind_arg and cases like max, min, etc which has multiple arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, you are saying that we can remove call to fill_optional_kind_arg as well as remove the ignore_null extra added keyword argument as well from all_args_evaluated?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, no, we need fill_optional_kind_arg, just check all_args_evaluated before filling optional arguments with nullptr.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, no, we need fill_optional_kind_arg, just check all_args_evaluated before filling optional arguments with nullptr.

I thought about this, and even if I do it before fill_optional_kind_arg as you mentioned that it would be filled with nullptr (for e.g. for min) that will happen even before fill_optional_kind_arg, as args is populated with nullptr in handle_intrinsic_node_args itself.

Copy link
Contributor

@Pranavchiku Pranavchiku left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments above, let me know if you wish more information about anything. I am marking this as draft.

@Pranavchiku Pranavchiku marked this pull request as draft March 21, 2024 13:29
@Pranavchiku
Copy link
Contributor

Pranavchiku commented Mar 21, 2024

The approach in this PR works well, no doubts, that looks good as well. Just if we are fixing it, and there is no hurry at moment ( it is not a blocker afaik ), let us do it correctly. I'll be happy to help.

@gxyd gxyd force-pushed the broadcast_intrinsic_functions branch from db2f763 to cdf5193 Compare March 22, 2024 08:31
@gxyd gxyd marked this pull request as ready for review March 22, 2024 10:50
@gxyd
Copy link
Contributor Author

gxyd commented Mar 22, 2024

Let me know how it looks now @Pranavchiku

print *, max(1, [-1, 2, 20])
print *, max([1, 2, 3], [1, 1, [2]])
print *, max([1, 2], -1, -4)
end program
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These need to have tests like if (...) error stop to ensure the answer is correct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test shape and size of the resulting array also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, I should've added it in the correct way, I'll change that.

print *, min([-1, 2, 3], 2, 5, [4, 4, 5], [5, -8, 7])
print *, min(1, [-1, 2, 20])
print *, min([1, 2, 3], [1, 1, [2]])
print *, min([1, 2], -1, -4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prints with GFortran:

          -1          -8           2
          -1           1           1
           1           1           2
          -4          -4

How does this work? I don't understand what min does with a combination of array and scalar arguments like this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it extends all arguments to be arrays like this:

min([-1, 2, 3], 2, 5, [4, 4, 5], [5, -8, 7])
->
min([-1, 2, 3], [2, 2, 2], [5, 5, 5], [4, 4, 5], [5, -8, 7])

And then it computes the minimum element-wise, so we get [-1, -8, 2]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct.

@certik
Copy link
Contributor

certik commented Mar 22, 2024

@gxyd thanks for working on this. I think there are many bugs like these and we need to fix them all, so this is extremely useful.

@gxyd
Copy link
Contributor Author

gxyd commented Mar 22, 2024

I think there are many bugs like these

@certik are there open issues for such bugs? (couldn't find any similar to this), let me know if there are more similar issues which need attention, I could take a look at those if needed.

@certik certik requested a review from Pranavchiku March 22, 2024 19:46
@Pranavchiku
Copy link
Contributor

I’ll review this PR in a while, around 11:30 IST.

Copy link
Contributor

@Pranavchiku Pranavchiku left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good, thanks for doing this!

@Pranavchiku Pranavchiku changed the title improve 'compiletime_broadcast_elemental_intrinsic' to support broadcasting for intrinsics enh: support compiletime_broadcast_elemental_intrinsic for multiple arguments Mar 23, 2024
@Pranavchiku Pranavchiku merged commit f63bb12 into lfortran:main Mar 23, 2024
21 checks passed
@gxyd gxyd deleted the broadcast_intrinsic_functions branch March 25, 2024 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Failure of elemental intrinsic functions on array input
3 participants