@@ -164,8 +164,9 @@ class HypergraphMatcher::Implementation {
164164 MatchPtr nextMatch_;
165165
166166 const EventDeduplication eventDeduplication_;
167- // Newly created matches for which the advanced deduplication algorithm has not yet been run.
168- // We sort them by sets they match to, and then by the chosen ordering function,
167+ // Newly created matches that have not yet been added to matchQueue_, allMatches_, etc.
168+ // This is needed either for event deduplication or to keep the order in which matches are added deterministic.
169+ // We sort them for event deduplication purposes by sets they match to, and then by the chosen ordering function,
169170 // so that each batch with identical inputs can be processed together, and it's obvious which copy should be retained.
170171 std::set<MatchPtr, MatchComparator> newMatches_;
171172
@@ -215,15 +216,18 @@ class HypergraphMatcher::Implementation {
215216 return getCurrentError () != None || abortRequested ();
216217 };
217218
219+ MatchStorage matchStorage;
218220 {
219221 // Only create threads if there is more than one rule
220222 const auto threadAcquisitionToken =
221223 Parallelism::acquire (Parallelism::HardwareType::StdCpu, static_cast <int >(rules_.size ()));
222224 const int & numThreadsToUse = threadAcquisitionToken->numThreads ();
225+ matchStorage = numThreadsToUse == 0 && eventDeduplication_ == EventDeduplication::None ? MatchStorage::Main
226+ : MatchStorage::NewMatches;
223227
224228 auto addMatchesForRuleRange = [=](RuleID start) {
225229 for (RuleID i = start; i < static_cast <RuleID>(rules_.size ()); i += numThreadsToUse) {
226- addMatchesForRule (tokenIDs, i, shouldAbort);
230+ addMatchesForRule (tokenIDs, i, shouldAbort, matchStorage );
227231 }
228232 };
229233
@@ -239,7 +243,7 @@ class HypergraphMatcher::Implementation {
239243 } else {
240244 // Single-threaded path
241245 for (RuleID i = 0 ; i < static_cast <RuleID>(rules_.size ()); ++i) {
242- addMatchesForRule (tokenIDs, i, shouldAbort);
246+ addMatchesForRule (tokenIDs, i, shouldAbort, matchStorage );
243247 }
244248 }
245249 }
@@ -254,6 +258,9 @@ class HypergraphMatcher::Implementation {
254258 removeIdenticalMatches (abortRequested);
255259 }
256260
261+ if (matchStorage == MatchStorage::NewMatches) {
262+ insertNewMatches ();
263+ }
257264 chooseNextMatch ();
258265 }
259266
@@ -324,14 +331,17 @@ class HypergraphMatcher::Implementation {
324331 }
325332
326333 private:
334+ enum class MatchStorage { Main, NewMatches };
335+
327336 void addMatchesForRule (const std::vector<TokenID>& tokenIDs,
328337 const RuleID& ruleID,
329- const std::function<bool ()>& shouldAbort) {
338+ const std::function<bool ()>& shouldAbort,
339+ const MatchStorage matchStorage) {
330340 const auto & ruleInputTokens = rules_[ruleID].inputs ;
331341 for (size_t i = 0 ; i < ruleInputTokens.size (); ++i) {
332342 const Match emptyMatch{ruleID, std::vector<TokenID>(ruleInputTokens.size (), -1 )};
333343 completeMatchesStartingWithInput (
334- emptyMatch, ruleInputTokens, rules_[ruleID].eventSelectionFunction , i, tokenIDs, shouldAbort);
344+ emptyMatch, ruleInputTokens, rules_[ruleID].eventSelectionFunction , i, tokenIDs, shouldAbort, matchStorage );
335345 }
336346 }
337347
@@ -340,14 +350,20 @@ class HypergraphMatcher::Implementation {
340350 const EventSelectionFunction eventSelectionFunction,
341351 const size_t nextInputIdx,
342352 const std::vector<TokenID>& potentialTokenIDs,
343- const std::function<bool ()>& shouldAbort) {
353+ const std::function<bool ()>& shouldAbort,
354+ const MatchStorage matchStorage) {
344355 for (const auto tokenID : potentialTokenIDs) {
345356 if (getCurrentError () != None) {
346357 return ;
347358 }
348359 if (isTokenUnused (incompleteMatch, tokenID)) {
349- attemptMatchTokenToInput (
350- incompleteMatch, partiallyMatchedInputs, eventSelectionFunction, nextInputIdx, tokenID, shouldAbort);
360+ attemptMatchTokenToInput (incompleteMatch,
361+ partiallyMatchedInputs,
362+ eventSelectionFunction,
363+ nextInputIdx,
364+ tokenID,
365+ shouldAbort,
366+ matchStorage);
351367 }
352368 }
353369 }
@@ -373,7 +389,8 @@ class HypergraphMatcher::Implementation {
373389 const EventSelectionFunction eventSelectionFunction,
374390 const size_t nextInputIdx,
375391 const TokenID potentialTokenID,
376- const std::function<bool ()>& shouldAbort) {
392+ const std::function<bool ()>& shouldAbort,
393+ const MatchStorage matchStorage) {
377394 // If WL wants to abort, abort
378395 if (shouldAbort ()) {
379396 setCurrentErrorIfNone (Error::Aborted);
@@ -401,7 +418,12 @@ class HypergraphMatcher::Implementation {
401418 }
402419
403420 if (isMatchComplete (newMatch)) {
404- insertMatch (newMatch);
421+ std::lock_guard<std::mutex> lock (matchMutex);
422+ if (matchStorage == MatchStorage::NewMatches) {
423+ newMatches_.insert (std::make_shared<Match>(newMatch));
424+ } else {
425+ insertMatch (std::make_shared<Match>(newMatch));
426+ }
405427 return ;
406428 }
407429
@@ -411,7 +433,8 @@ class HypergraphMatcher::Implementation {
411433 eventSelectionFunction,
412434 nextInputIdxAndCandidateTokens.first ,
413435 nextInputIdxAndCandidateTokens.second ,
414- shouldAbort);
436+ shouldAbort,
437+ matchStorage);
415438 }
416439
417440 bool isSpacelikeSeparated (const TokenID newToken, const std::vector<TokenID>& previousTokens) {
@@ -426,12 +449,20 @@ class HypergraphMatcher::Implementation {
426449 return true ;
427450 }
428451
429- void insertMatch (const Match& newMatch) {
430- // careful, don't create different pointers to the same match!
431- const auto matchPtr = std::make_shared<Match>(newMatch);
432-
433- std::lock_guard<std::mutex> lock (matchMutex);
452+ void insertNewMatches () {
453+ std::vector<MatchPtr> sortedMatches (std::make_move_iterator (newMatches_.begin ()),
454+ std::make_move_iterator (newMatches_.end ()));
455+ newMatches_.clear ();
456+ // We should sort them in the same order they would be added if the evaluation was sequential.
457+ std::sort (sortedMatches.begin (), sortedMatches.end (), [](const MatchPtr& first, const MatchPtr& second) {
458+ return first->rule < second->rule ;
459+ });
460+ for (const auto & match : sortedMatches) {
461+ insertMatch (match);
462+ }
463+ }
434464
465+ void insertMatch (const MatchPtr matchPtr) {
435466 if (!allMatches_.insert (matchPtr).second ) {
436467 return ;
437468 }
@@ -447,10 +478,6 @@ class HypergraphMatcher::Implementation {
447478 tokensToMatches_[token].insert (matchPtr);
448479 }
449480 }
450-
451- if (eventDeduplication_ == EventDeduplication::SameInputSetIsomorphicOutputs) {
452- newMatches_.insert (matchPtr);
453- }
454481 }
455482
456483 static bool isMatchComplete (const Match& match) {
@@ -552,30 +579,31 @@ class HypergraphMatcher::Implementation {
552579 void removeIdenticalMatches (const std::function<bool ()>& abortRequested) {
553580 std::unordered_set<TokenID> currentInputsSet;
554581 std::vector<MatchPtr> addedSameInputMatches;
555- for (const auto & newMatch : newMatches_) {
556- if (!sameInputSet (newMatch , currentInputsSet)) {
582+ for (auto newMatchIt = newMatches_. begin (); newMatchIt != newMatches_. end (); ) {
583+ if (!sameInputSet (*newMatchIt , currentInputsSet)) {
557584 // matches are ordered by their input sets, so if it's different, a batch with the new inputs is starting.
558585 currentInputsSet.clear ();
559- currentInputsSet.insert (newMatch ->inputTokens .begin (), newMatch ->inputTokens .end ());
586+ currentInputsSet.insert ((*newMatchIt) ->inputTokens .begin (), (*newMatchIt) ->inputTokens .end ());
560587 addedSameInputMatches.clear ();
561588 }
562589
563590 bool matchAppearedBefore = false ;
564591 for (const auto & addedMatch : addedSameInputMatches) {
565- if (sameOutcomeAssumingSameInputs (newMatch , addedMatch, abortRequested)) {
592+ if (sameOutcomeAssumingSameInputs (*newMatchIt , addedMatch, abortRequested)) {
566593 matchAppearedBefore = true ;
594+ break ;
567595 }
568596 if (getCurrentError () != None) {
569597 return ;
570598 }
571599 }
572600 if (matchAppearedBefore) {
573- deleteMatch (newMatch );
601+ newMatchIt = newMatches_. erase (newMatchIt );
574602 } else { // same input set, but a different outcome
575- addedSameInputMatches.emplace_back (newMatch);
603+ addedSameInputMatches.emplace_back (*newMatchIt);
604+ ++newMatchIt;
576605 }
577606 }
578- newMatches_.clear ();
579607 }
580608
581609 // Checks if the input token IDs in the match are the same as referenceInputTokens
0 commit comments