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

[Core] Introducing proper insert method to PointerVectorSet #12087

Merged
merged 31 commits into from
Jun 10, 2024

Conversation

sunethwarna
Copy link
Member

@sunethwarna sunethwarna commented Feb 19, 2024

πŸ“ Description
This PR is introducing the PointerVectorSet::insert method which is suppose to replace the currently used PointerVectorSet::push_back method. This is one of many PRs addressing the issue #11363 .

❗ This is one of many PRs which will address the points mentioned in #11363.

Followings chages are done:

  • The insert(pointer) method is changed not to sort anymore, and to put the new element in the correct position while keeping the sortedness in the PointerVectorSet. This assumes PointerVectorSet is already in a sorted state.
  • The insert(begin, end) method also changed not to sort the existing data, it sorts the incoming range and make it unique before inserting them to the mData. This also assumes PointerVectorSet is already in sorted state.
  • The insert(position_hint, pointer) uses the position hint if it is valid, otherwise it discards the position_hint and puts the new pointer in the correct place. This also assumes PointerVectorSet is already in sorted state.
  • The default TGetKeyType is changed to return memory locations if the provided TDataType does not have operator< and operator== defined. Otherwise, TGetKeyType returns the exact input object.

πŸ†• Changelog

  • Modify insert methods to assume PointerVectorSet is always sorted when inserting new elements.
  • Introduced SetFunction to get the memory location if operator< and operator== not defined, otherwise it returns the input object..
  • Changed default identification method of PointerVectorSet to be based on memory or exact objects by using SetFunction
  • Added tests for introduced insert methods

@sunethwarna sunethwarna requested a review from a team as a code owner February 19, 2024 14:08
@sunethwarna sunethwarna self-assigned this Feb 19, 2024
@sunethwarna sunethwarna requested review from a team and removed request for a team February 19, 2024 14:09
@sunethwarna sunethwarna added Behaviour Change changes behaviour but not the API Kratos Core C++ labels Feb 19, 2024
@ddiezrod
Copy link
Contributor

ddiezrod commented Feb 23, 2024

ping @KratosMultiphysics/altair so that everyone is aware as this is an important change,

Copy link
Member

@pooyan-dadvand pooyan-dadvand left a comment

Choose a reason for hiding this comment

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

Thanks @sunethwarna! I am not sure about the set function implementation and I think the insert implementation is not coherent. Also, I like more the implementation of the range inserts than the value insert itself. I think the range ones are more clear and have less ifs


*i = pData;
return i;
} else if (EqualKeyTo(KeyOf(*position_hint))(*ptr_begin())) {
Copy link
Member

Choose a reason for hiding this comment

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

I think here should be else if (position_hint == cbegin())

Copy link
Member Author

Choose a reason for hiding this comment

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

I can change it :). As far as i see, current implementation is the same, except, it checks the value of the begin rather than the iterator.

Copy link
Member

Choose a reason for hiding this comment

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

But the value check should come in the main if like other statements of upper if elses

Copy link
Member Author

Choose a reason for hiding this comment

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

makes sense and done :)

*/
template <class InputIterator>
void insert(InputIterator First, InputIterator Last)
void insert(InputIterator first, InputIterator last)
Copy link
Member

Choose a reason for hiding this comment

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

Shall we have an additional argument (or a separate method) for insert_sorted_values?

Copy link
Member Author

Choose a reason for hiding this comment

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

It is little bit dangerous. Thats why I put insert(const PointVectorSet& rOther) overload to avoid doing sorting and making it unique. insert(end(), value) is having O(N) cost if we are inserting a sorted array which can be used to construct a quick sorted PointerVectorSet.

Copy link
Contributor

Choose a reason for hiding this comment

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

Shall we have an additional argument (or a separate method) for insert_sorted_values?

definitely not. The whole point of refactoring PointerVectorSet is to enforce its contract (sortedness and uniqueness) regardless of user input.

Copy link
Member

Choose a reason for hiding this comment

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

Agree with @sunethwarna that adding ordered set of entities is far faster than insert without assuming that. However creating the first PointerVectorSet would involve the sort.

Disagree with @matekelemen in the priority of robustness over performance for this case that the complexity of the algorithm changes from N to NlogN. Please note that in most of the mesh generation modelers we are adding millions of entities (to sub-modelparts) which we create with ordered indices (starting from the largest) and the first sort of those is an unnecessary operation

Nevertheless, I agree that this can be added afterward

* @param data The input object of type TDataType.
* @return A non-const reference pointer or reference to the same object as provided in the parameter.
*/
std::conditional_t<HasOperatorsDefined, TDataType&, TDataType*> operator()(TDataType& rData)
Copy link
Member

Choose a reason for hiding this comment

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

This is somehow weird to return different types and the final objective is not clear if one is not familiar with the implementation. I think it would be more clear to have the operator < and == defined here properly depending if the value has those defines or not

Copy link
Member Author

Choose a reason for hiding this comment

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

Then the question is, what is the key_type of a PointerVectorSet<SomeDataType> where the SomeDataType does not have the operator< and/or operator== defined?

We need to put somewhere this conditional_t if we want to illustrate that if they use PointerVectorSet with TDataType which does not have the operator< and operator== defined.

The above solution was the least intrusive AFAIK.

Copy link
Member

Choose a reason for hiding this comment

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

Now I see, This is the GetKey function which make sense to return the pointer instead of value if the value is not comparable. Then I would add the GetKey(or an equivalent) to the name to be more clear. (I admit that mine wasn't clear neither πŸ˜…)

I agree with the behavior then!

@sunethwarna
Copy link
Member Author

@pooyan-dadvand I addressed your suggestions/comments. Could you have a look?

Copy link
Member

@pooyan-dadvand pooyan-dadvand left a comment

Choose a reason for hiding this comment

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

Already in a good shape! Some minor comments

{
// all 4 bounds are equal, hence this can be inserted without checking further
if (lower_bound_first == mData.end()) {
for (auto it = first; it != last; ++it) {
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't be better to reserve first?

Copy link
Member Author

Choose a reason for hiding this comment

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

done :)

return iterator(mData.end() - 1);
} else {
// given position is invalid. Hence, discarding the hint.
return insert(value);
Copy link
Member

Choose a reason for hiding this comment

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

Maybe a minor improvement, but I would not calling the insert here and just add the last statement of ifs in insert to avoid reevaluating for push_back

Copy link
Member Author

Choose a reason for hiding this comment

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

That's not possible, I need to add all if statements, because we need to do all the checks if the hint is discarded.

Copy link
Member

Choose a reason for hiding this comment

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

I see....

return mData.insert(mData.begin(), value);
} else {
// given position is invalid. Hence, discarding the hint.
return insert(value);
Copy link
Member

Choose a reason for hiding this comment

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

same here

Copy link
Member Author

Choose a reason for hiding this comment

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

As explained before.

return mData.insert(mData.begin() + (position_hint - cbegin()), value);
} else {
// given position is invalid. Hence, discarding the hint.
return insert(value);
Copy link
Member

Choose a reason for hiding this comment

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

same here

Copy link
Member Author

Choose a reason for hiding this comment

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

As explained before.

* @author Suneth Warnakulasuriya
*/
template<class TDataType>
class SetFunction
Copy link
Member

Choose a reason for hiding this comment

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

Would you please give a more descriptive name to this function? PointerSetGetKey?

Copy link
Member Author

Choose a reason for hiding this comment

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

done :)... Changed to KeyGenerator.

@sunethwarna
Copy link
Member Author

@pooyan-dadvand I addressed your comments. Could you take a look :)

Copy link
Member

@pooyan-dadvand pooyan-dadvand left a comment

Choose a reason for hiding this comment

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

Thanks!

return iterator(mData.end() - 1);
} else {
// given position is invalid. Hence, discarding the hint.
return insert(value);
Copy link
Member

Choose a reason for hiding this comment

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

I see....

@sunethwarna sunethwarna merged commit 384b3cd into master Jun 10, 2024
11 checks passed
@sunethwarna sunethwarna deleted the core/pointer_vector_set/introduce_insert branch June 10, 2024 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Behaviour Change changes behaviour but not the API C++ Kratos Core
Projects
Status: βœ… Done
Development

Successfully merging this pull request may close these issues.

None yet

5 participants