SimilarityFilter.cpp
Go to the documentation of this file.
1#include "SimilarityFilter.h"
2
3#include <algorithm>
4#include <vector>
5
7
20
22{
23 bool
25 {
26 // Thread safety: lock the filter mutex for all mutable state access
27 std::lock_guard<std::mutex> lock(filterMutex_);
28
29 auto start = std::chrono::high_resolution_clock::now();
30
31 const std::string entityId = e.id().getEntityID().str();
32 auto& acceptedSnapshots = lastAcceptedSnapshots[entityId];
33
34 if (acceptedSnapshots.empty())
35 {
36 acceptedSnapshots.push_back(e);
37 stats.accepted++;
38 auto end = std::chrono::high_resolution_clock::now();
39 stats.end_time = end;
40 stats.additional_time += (end - start);
41 return true;
42 }
43
44 // Check if the new snapshot is similar to any of the last accepted snapshots
45 bool foundSimilarSnapshot = false;
46 for (const auto& oldSnapshot : acceptedSnapshots)
47 {
48 const float similarity = calculateSnapshotSimilarity(oldSnapshot, e);
49 // ARMARX_INFO << "Snapshot similarity: " << similarity;
50 if (similarity >= threshold)
51 {
52 foundSimilarSnapshot = true;
53 break;
54 }
55 }
56
57 if (foundSimilarSnapshot)
58 {
59 stats.rejected++;
60 }
61 else
62 {
63 acceptedSnapshots.push_back(e);
64 if (acceptedSnapshots.size() > numSnapshots)
65 {
66 acceptedSnapshots.pop_front();
67 }
68 stats.accepted++;
69 }
70
71 auto end = std::chrono::high_resolution_clock::now();
72 stats.end_time = end;
73 stats.additional_time += (end - start);
74
75 // if (!foundSimilarSnapshot) ARMARX_INFO << "ACCEPTED";
76
77 return !foundSimilarSnapshot;
78 }
79
80 float
81 SnapshotSimilarityFilter::calculateSnapshotSimilarity(
82 const armem::wm::EntitySnapshot& oldSnapshot,
83 const armem::wm::EntitySnapshot& newSnapshot)
84 {
85 std::vector<aron::data::VariantPtr> oldInstances;
86 std::vector<aron::data::VariantPtr> newInstances;
87
88 oldSnapshot.forEachInstance(
89 [&oldInstances](armem::wm::EntityInstance& i)
90 {
91 oldInstances.push_back(i.data());
92 }
93 );
94
95 newSnapshot.forEachInstance(
96 [&newInstances](armem::wm::EntityInstance& i)
97 {
98 newInstances.push_back(i.data());
99 }
100 );
101
102 if (oldInstances.size() != newInstances.size())
103 {
104 return 0.0f;
105 }
106
107 float weightedTotal = 0.0f;
108 float totalImportance = 0.0f;
109 for (size_t i = 0; i < newInstances.size(); i++)
110 {
111 const auto result =
112 calculateInstanceSimilarity(oldInstances[i], newInstances[i], 1.0f);
113 weightedTotal += result.similarity * result.importance;
114 totalImportance += result.importance;
115 }
116
117 return totalImportance > 0.0f ? weightedTotal / totalImportance : 1.0f;
118 }
119
120 SnapshotSimilarityFilter::SimilarityResult
121 SnapshotSimilarityFilter::calculateInstanceSimilarity(
122 const aron::data::VariantPtr& oldData,
123 const aron::data::VariantPtr& newData,
124 float parentImportance)
125 {
126 if (!oldData && !newData)
127 {
128 return {1.0f, parentImportance};
129 }
130 if (!oldData || !newData)
131 {
132 return {0.0f, parentImportance};
133 }
134
135 const float importance = newData->getImportance().has_value() ?
136 parentImportance * newData->getImportance().value() : parentImportance;
137
138 // ignoring field if importance is 0
139 if (importance <= 0.0f)
140 {
141 return {1.0f, 0.0f};
142 }
143
144 auto oldDesc = oldData->getDescriptor();
145 auto newDesc = newData->getDescriptor();
146 if (oldDesc != newDesc)
147 {
148 return {0.0f, importance};
149 }
150
151 // comparison can be extended for new types
152 // recursive in case of containers, LIST is order-sensitive
153 switch(newDesc)
154 {
156 {
157 auto oldInt = aron::data::Int::DynamicCastAndCheck(oldData);
158 auto newInt = aron::data::Int::DynamicCastAndCheck(newData);
159 return {
161 importance};
162 }
164 {
165 auto oldFloat = aron::data::Float::DynamicCastAndCheck(oldData);
166 auto newFloat = aron::data::Float::DynamicCastAndCheck(newData);
167 return {
169 importance};
170 }
172 {
173 auto oldDouble = aron::data::Double::DynamicCastAndCheck(oldData);
174 auto newDouble = aron::data::Double::DynamicCastAndCheck(newData);
175 return {
177 importance};
178 }
180 {
181 auto oldLong = aron::data::Long::DynamicCastAndCheck(oldData);
182 auto newLong = aron::data::Long::DynamicCastAndCheck(newData);
183 return {
185 importance};
186 }
188 {
189 auto oldString = aron::data::String::DynamicCastAndCheck(oldData);
190 auto newString = aron::data::String::DynamicCastAndCheck(newData);
191 return {
193 importance};
194 }
196 {
197 auto oldBool = aron::data::Bool::DynamicCastAndCheck(oldData);
198 auto newBool = aron::data::Bool::DynamicCastAndCheck(newData);
199 return {
201 importance};
202 }
204 {
205 auto oldNdarr = aron::data::NDArray::DynamicCastAndCheck(oldData);
206 auto newNdarr = aron::data::NDArray::DynamicCastAndCheck(newData);
207 return {
209 importance};
210 }
212 {
213 auto oldList = aron::data::List::DynamicCastAndCheck(oldData);
214 auto newList = aron::data::List::DynamicCastAndCheck(newData);
215 const auto& oldElems = oldList->getElements();
216 const auto& newElems = newList->getElements();
217
218 if (oldElems.size() != newElems.size())
219 {
220 return {0.0f, importance};
221 }
222
223 float weightedTotal = 0.0f;
224 float totalImportance = 0.0f;
225 for (size_t i = 0; i < newElems.size(); i++)
226 {
227 const auto result =
228 calculateInstanceSimilarity(oldElems[i], newElems[i], importance);
229 weightedTotal += result.similarity * result.importance;
230 totalImportance += result.importance;
231 }
232 const float similarity = totalImportance > 0.0f ? weightedTotal / totalImportance : 1.0f;
233 return {similarity, importance};
234 }
236 {
237 auto oldDict = aron::data::Dict::DynamicCastAndCheck(oldData);
238 auto newDict = aron::data::Dict::DynamicCastAndCheck(newData);
239 auto oldKeys = oldDict->getAllKeys();
240 auto newKeys = newDict->getAllKeys();
241
242 if (oldKeys.size() != newKeys.size())
243 {
244 return {0.0f, importance};
245 }
246
247 float weightedTotal = 0.0f;
248 float totalImportance = 0.0f;
249 for (const auto& key : newKeys)
250 {
251 if (!oldDict->hasElement(key))
252 {
253 return {0.0f, importance};
254 }
255
256 const auto result =
257 calculateInstanceSimilarity(oldDict->at(key), newDict->at(key), importance);
258 weightedTotal += result.similarity * result.importance;
259 totalImportance += result.importance;
260 }
261 const float similarity = totalImportance > 0.0f ? weightedTotal / totalImportance : 1.0f;
262 return {similarity, importance};
263 }
264 default:
265 {
266 ARMARX_INFO << "data-type not yet supported.";
267 return {0.0f, importance};
268 }
269 }
270 }
271
272 void
273 SnapshotSimilarityFilter::configure(const nlohmann::json& json)
274 {
275 std::lock_guard<std::mutex> lock(filterMutex_);
276
277 if (json.find(PARAM_THRESHOLD) != json.end())
278 {
279 threshold = json.at(PARAM_THRESHOLD);
280 // ARMARX_INFO << VAROUT(threshold);
281 stats.additional_info += "Similarity threshold: ";
282 stats.additional_info += std::to_string(threshold);
283 }
284
285 if (json.find(PARAM_NUM_SNAPSHOTS) != json.end())
286 {
287 numSnapshots = json.at(PARAM_NUM_SNAPSHOTS);
288 }
289
290 numSnapshots = std::max<std::size_t>(1, numSnapshots);
291 stats.number_of_compared_objects = static_cast<int>(numSnapshots);
292 stats.start_time = std::chrono::high_resolution_clock::now();
293 // stats.similarity_type = aron::similarity::NDArraySimilarity::Type::NONE;
294 }
295
298 {
299 std::lock_guard<std::mutex> lock(filterMutex_);
300 return stats;
301 }
302
303 std::string
305 {
306 return this->NAME;
307 }
308
309} // namespace armarx::armem::server::ltm::processor::filter
std::string str(bool escapeDelimiters=true) const
Get a string representation of this memory ID.
Definition MemoryID.cpp:102
MemoryID getEntityID() const
Definition MemoryID.cpp:310
bool forEachInstance(InstanceFunctionT &&func)
std::mutex filterMutex_
Mutex for thread-safe access to filter state (stats and derived class state) Derived classes should l...
Definition Filter.h:61
virtual bool accept(const armem::wm::EntitySnapshot &e, bool simulatedVersion) override
Client-side working entity instance.
Client-side working memory entity snapshot.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:179
std::shared_ptr< Variant > VariantPtr
float calculateSimilarity(const aron::data::NDArray &oldValue, const aron::data::NDArray &newValue)
float calculateSimilarity(const aron::data::Bool &oldValue, const aron::data::Bool &newValue)