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
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e1702fd
implement set_address_identity_function
sunethwarna Feb 19, 2024
0520260
use set_address_identity_function
sunethwarna Feb 19, 2024
abaeedd
add insert methods to ptrvecset
sunethwarna Feb 19, 2024
4b34963
add insert tests
sunethwarna Feb 19, 2024
1a226e4
set mSortedPartSize = mData.size()
sunethwarna Feb 19, 2024
770d666
add the inserts to the end
sunethwarna Feb 19, 2024
b3a9a76
revert elimination builder and solver changes
sunethwarna Feb 19, 2024
b0ef86b
change dofarraytype
sunethwarna Feb 19, 2024
f5a7f05
fix ranged insert bug with end
sunethwarna Feb 19, 2024
8dfbe09
fix mmg reordering
sunethwarna Feb 20, 2024
f2f3dcc
fix dof updater
sunethwarna Feb 20, 2024
1c9af8e
add additional reserve
sunethwarna Feb 20, 2024
d0ef0e7
add another insertion method
sunethwarna Feb 20, 2024
3544a66
Merge remote-tracking branch 'origin/master' into core/pointer_vector…
sunethwarna Feb 22, 2024
9c00f71
introduce set function
sunethwarna Feb 22, 2024
8b04cec
define IndexedObject operators
sunethwarna Feb 22, 2024
e86088b
use set function in PointerVectorSet
sunethwarna Feb 22, 2024
5760f73
add operator== to closest_point
sunethwarna Feb 23, 2024
bc46807
Merge remote-tracking branch 'origin/master' into core/pointer_vector…
sunethwarna Feb 26, 2024
6e7a68f
use cbegin()
sunethwarna Feb 26, 2024
f604617
add additional insert mechanism and test
sunethwarna Feb 27, 2024
b1bc015
Merge remote-tracking branch 'origin/master' into core/pointer_vector…
sunethwarna Mar 6, 2024
1437a25
remove use of *::base()
sunethwarna Mar 6, 2024
8c58384
Merge remote-tracking branch 'origin/master' into core/pointer_vector…
sunethwarna May 29, 2024
f84b135
add reserve
sunethwarna May 29, 2024
b82885c
avoid calling insert
sunethwarna May 29, 2024
0555c33
change name
sunethwarna Jun 5, 2024
4e5f741
add additional iterator tests
sunethwarna Jun 5, 2024
2cfaa7b
remove operator def
sunethwarna Jun 5, 2024
ebea638
Merge remote-tracking branch 'origin/master' into core/pointer_vector…
sunethwarna Jun 5, 2024
359f87e
revert
sunethwarna Jun 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 103 additions & 49 deletions kratos/containers/pointer_vector_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
// Project includes
#include "includes/define.h"
#include "includes/serializer.h"
#include "containers/set_identity_function.h"
#include "containers/set_address_identity_function.h"

namespace Kratos
{
Expand Down Expand Up @@ -63,7 +63,7 @@ namespace Kratos
* @author Pooyan Dadvand
*/
template<class TDataType,
class TGetKeyType = SetIdentityFunction<TDataType>,
class TGetKeyType = SetAddressIdentityFunction<TDataType>,
class TCompareType = std::less<decltype(std::declval<TGetKeyType>()(std::declval<TDataType>()))>,
class TEqualType = std::equal_to<decltype(std::declval<TGetKeyType>()(std::declval<TDataType>()))>,
class TPointerType = typename TDataType::Pointer,
Expand Down Expand Up @@ -126,8 +126,8 @@ class PointerVectorSet final
PointerVectorSet(TInputIteratorType First, TInputIteratorType Last, size_type NewMaxBufferSize = 1)
: mSortedPartSize(size_type()), mMaxBufferSize(NewMaxBufferSize)
{
for (; First != Last; ++First)
insert(begin(), *First);
mData.reserve(std::distance(First, Last));
RiccardoRossi marked this conversation as resolved.
Show resolved Hide resolved
insert(First, Last);
}

/**
Expand All @@ -136,7 +136,7 @@ class PointerVectorSet final
*/
PointerVectorSet(const PointerVectorSet& rOther)
: mData(rOther.mData), mSortedPartSize(rOther.mSortedPartSize), mMaxBufferSize(rOther.mMaxBufferSize) {}

/**
* @brief Constructs a PointerVectorSet from a container.
* @details This constructor initializes a PointerVectorSet with elements from a container.
Expand Down Expand Up @@ -558,54 +558,108 @@ class PointerVectorSet final
mSortedPartSize = mData.size();
}

/**
* @brief Inserts a pointer at the specified position.
* @details This function inserts a given pointer at the specified position in the set. It also maintains
* the sorting order and updates mSortedPartSize if necessary.
* @param Position An iterator pointing to the position where the pointer should be inserted.
* @param pData The pointer to be inserted.
/**
* @brief Inserts a pointer.
* @details This function inserts a given pointer such that the resulting PointerVectorSet
* is kept sorted. If there exists already a pointer with a key same as the key of the value, then
* this will return iterator of that existing pointer (The value will not be inserted.)
* @param value The pointer to be inserted.
* @return An iterator pointing to the inserted element.
*/
iterator insert(iterator Position, const TPointerType pData)
{
ptr_iterator sorted_part_end;

key_type key = KeyOf(*pData);

if (mData.size() - mSortedPartSize >= mMaxBufferSize) {
Sort();
sorted_part_end = mData.end();
} else
sorted_part_end = mData.begin() + mSortedPartSize;

ptr_iterator i(std::lower_bound(mData.begin(), sorted_part_end, key, CompareKey()));
if (i == sorted_part_end) {
mSortedPartSize++;
return mData.insert(sorted_part_end, pData);
iterator insert(const TPointerType& value)
{
auto itr_pos = std::lower_bound(mData.begin(), mData.end(), KeyOf(*value), CompareKey());
if (itr_pos == mData.end()) {
// the position to insert is at the end.
mData.push_back(value);
return iterator(mData.end() - 1);
} else if (EqualKeyTo(KeyOf(*value))(*itr_pos)) {
// already found existing element with the same key, hence returning the existing element.
return iterator(itr_pos);
} else {
// insert the new value before the itr_pos.
return mData.insert(itr_pos, value);
}
}

if (!EqualKeyTo(key)(*i))
if ((i = std::find_if(sorted_part_end, mData.end(), EqualKeyTo(key))) == mData.end()) {
mData.push_back(pData);
/**
* @brief Inserts a pointer at the specified position.
* @details This function inserts a given pointer. If the given position_hint is valid, then
* it uses that to insert the value, otherwise the position_hint is discarded to maintain the dataset
* sorted. If there is an existing element with the same key as in the value, then an iterator for
* the existing element is returned.
* @param position_hint An iterator pointing to the position where the pointer may be inserted.
* @param value The pointer to be inserted.
* @return An iterator pointing to the inserted element.
*/
iterator insert(const_iterator position_hint, const TPointerType& value)
{
if (empty()) {
// the dataset is empty. So use push back.
mData.push_back(value);
RiccardoRossi marked this conversation as resolved.
Show resolved Hide resolved
return iterator(mData.end() - 1);
} else if (position_hint == cend()) {
// trying to insert at the end.
if (KeyOf(*(position_hint - 1)) < KeyOf(*value)) {
// key at the position hint is less than the value of key. Hence position hint
// is valid. So using the push back.
mData.push_back(value);
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

@sunethwarna sunethwarna May 29, 2024

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....

}

*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

@sunethwarna sunethwarna Feb 26, 2024

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

@sunethwarna sunethwarna Feb 26, 2024

Choose a reason for hiding this comment

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

makes sense and done :)

// trying to insert at the front.
if (KeyOf(*value) < KeyOf(*position_hint)) {
// key at the position hint is greater than the value of key. Hence position hint
// is valid. So using the push back.
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

@sunethwarna sunethwarna May 29, 2024

Choose a reason for hiding this comment

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

As explained before.

}
} else {
// trying to insert at an arbitrary position.
if (KeyOf(*value) < KeyOf(*position_hint) && (KeyOf(*(position_hint - 1)) < KeyOf(*value))) {
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

@sunethwarna sunethwarna May 29, 2024

Choose a reason for hiding this comment

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

As explained before.

}
}
}

/**
* @brief Insert elements from a range of iterators.
* @details This function inserts elements from a range defined by the iterators `First` and `Last`
* into the set. It uses the `insert` function to insert each element.
* @param First An input iterator pointing to the beginning of the range to insert.
* @param Last An input iterator pointing to the end of the range to insert.
* @details This function inserts element pointers from a range defined by the iterators `first` and `last`
* into the set. This will not insert any elements in the range, if there exists an element with a key
* which is equal to an element's key in the input range.
* @param first An input iterator pointing to the beginning of the range to insert.
* @param last An input iterator pointing to the end of the range to insert.
*/
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

{
for (; First != Last; ++First)
insert(begin(), *First);
// first sorts the input iterators and make the input unique.
std::sort(first, last, CompareKey());
auto new_last = std::unique(first, last, EqualKeyTo());

if (empty()) {
for (; first != new_last; ++first) {
mData.push_back(*first);
}
} else {
auto p_current_itr = mData.begin();
// now add the new elements
for (; first != new_last; ++first) {
// find the lower bound element.
p_current_itr = std::lower_bound(p_current_itr, mData.end(), KeyOf(**first), CompareKey());
if (!EqualKeyTo(KeyOf(**first))(*p_current_itr)) {
p_current_itr = mData.insert(p_current_itr, *first);
}
}
}
}

/**
Expand Down Expand Up @@ -789,32 +843,32 @@ class PointerVectorSet final
/**
* @brief Get the maximum size of buffer used in the container
*/
size_type GetMaxBufferSize() const
size_type GetMaxBufferSize() const
{
return mMaxBufferSize;
}

/**
/**
* @brief Set the maximum size of buffer used in the container.
* @details This container uses a buffer which keep data unsorted. After buffer size arrived to the MaxBufferSize it will sort all container and empties buffer.
* @param NewSize Is the new buffer maximum size.
* @param NewSize Is the new buffer maximum size.
*/
void SetMaxBufferSize(const size_type NewSize)
{
mMaxBufferSize = NewSize;
}

/**
* @brief Get the sorted part size of buffer used in the container.
* @brief Get the sorted part size of buffer used in the container.
*/
size_type GetSortedPartSize() const
size_type GetSortedPartSize() const
{
return mSortedPartSize;
}

/**
/**
* @brief Set the sorted part size of buffer used in the container.
* @param NewSize Is the new buffer maximum size.
* @param NewSize Is the new buffer maximum size.
*/
void SetSortedPartSize(const size_type NewSize)
{
Expand Down Expand Up @@ -980,10 +1034,10 @@ class PointerVectorSet final

/// The data container holding the elements.
TContainerType mData;

/// The size of the sorted portion of the data.
size_type mSortedPartSize;

/// The maximum buffer size for data storage.
size_type mMaxBufferSize;

Expand Down
70 changes: 70 additions & 0 deletions kratos/containers/set_address_identity_function.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// | / |
// ' / __| _` | __| _ \ __|
// . \ | ( | | ( |\__ `
// _|\_\_| \__,_|\__|\___/ ____/
// Multi-Physics
//
// License: BSD License
// Kratos default license: kratos/license.txt
//
// Main authors: Pooyan Dadvand
// Suneth Warnakulasuriya
//
//

#pragma once

// System includes
#include <functional>

// External includes

// Project includes

namespace Kratos
{
///@name Kratos Classes
///@{

/**
* @class SetAddressIdentityFunction
* @brief A functor that serves as the identity function for memory locations.
* @details This class provides two overloaded operator() functions, one for non-const objects
* and another for const objects. The operator() returns the input objects memory location as it is,
* effectively acting as an identity function for address.
* The purpose of this functor is to allow objects' memory locations to be used as keys in sets or other
* containers that require comparison. By using this functor, you can avoid defining
* custom comparison functions or operators when the object itself can be considered
* for comparison.
* @tparam TDataType The data type of the object that the functor operates on.
* @author Pooyan Dadvand
* @author Suneth Warnakulasuriya
*/
template<class TDataType>
class SetAddressIdentityFunction
{
public:
/**
* @brief Operator that returns a non-const reference pointer to the input object.
* @param data The input object of type TDataType.
* @return A non-const reference pointer to the same object as provided in the parameter.
*/
TDataType* operator()(TDataType& data)
{
return &data;
}

/**
* @brief Operator that returns a const reference pointer to the input object.
* @param data The input object of type TDataType.
* @return A const reference pointer to the same object as provided in the parameter.
*/
TDataType const * operator()(const TDataType& data) const
{
return &data;
}
};

///@}

} // namespace Kratos.
Loading
Loading