258 const std::vector<COMPONENT*>& aStructuralMatches,
260 std::vector<TOPOLOGY_MISMATCH_REASON>& aMismatchReasons,
261 const std::atomic<bool>* aCancelled )
263 if( aCancelled && aCancelled->load( std::memory_order_relaxed ) )
268 aMismatchReasons.clear();
269 std::vector<COMPONENT*> matches;
270 int candidatesChecked = 0;
271 int skippedLocked = 0;
277 double netCheckMs = 0.0;
279 for(
COMPONENT* cmpTarget : aStructuralMatches )
281 if( partialMatches.
m_locked.find( cmpTarget ) != partialMatches.
m_locked.end() )
290 cmpTarget->m_reference );
294 localReason.
m_candidate = cmpTarget->GetParent()->GetReferenceAsString();
299 netCheckMs += timerNet.
msecs();
304 matches.push_back( cmpTarget );
308 wxLogTrace(
traceTopoMatch, wxT(
"Reject [net topo mismatch]\n" ) );
309 aMismatchReasons.push_back( localReason );
315 std::unordered_map<COMPONENT*, double> simScores;
316 simScores.reserve( matches.size() );
322 for(
size_t i = 0; i < aRef->
m_pins.size(); i++ )
324 if( aRef->
m_pins[i]->GetNetCode() == match->m_pins[i]->GetNetCode() )
328 simScores[match] =
static_cast<double>( n ) /
static_cast<double>( aRef->
m_pins.size() );
331 std::sort( matches.begin(), matches.end(),
334 double simA = simScores[a];
335 double simB = simScores[b];
340 return a->GetParent()->GetReferenceAsString()
341 < b->GetParent()->GetReferenceAsString();
346 if( matches.empty() )
350 reason.
m_reason =
_(
"No compatible component found in the target area." );
352 if( aMismatchReasons.empty() )
353 aMismatchReasons.push_back( reason );
359 wxT(
" findMatch '%s' (%d pins): %s total, checked %d/%d structural, "
360 "netCheck %0.3f ms, score %0.3f ms, %d matches" ),
362 candidatesChecked, (
int) aStructuralMatches.size(),
363 netCheckMs, timerScore.
msecs(),
364 (
int) matches.size() );
405 auto getSymbolInstanceUuid =
416 const KIID& symbolUuid =
path.back();
422 const KIID refSymbolUuid = getSymbolInstanceUuid( refFp );
423 wxString candidateSymbolUuids;
424 wxString matchingSymbolCandidates;
425 int symbolUuidHitCount = 0;
426 int uniqueMatchIdx = -1;
438 for(
size_t i = 0; i < aMatches.size(); i++ )
440 FOOTPRINT* candidateFp = aMatches[i]->GetParent();
442 const KIID candidateSymbolUuid = getSymbolInstanceUuid( candidateFp );
445 candidateSymbolUuids += wxT(
", " );
447 if( candidateSymbolUuid ==
niluuid )
448 candidateSymbolUuids += candidateRef + wxT(
"=<none>" );
450 candidateSymbolUuids += candidateRef + wxT(
"=" ) + candidateSymbolUuid.
AsString();
452 if( candidateSymbolUuid == refSymbolUuid )
454 if( uniqueMatchIdx < 0 )
455 uniqueMatchIdx =
static_cast<int>( i );
457 symbolUuidHitCount++;
459 if( !matchingSymbolCandidates.IsEmpty() )
460 matchingSymbolCandidates += wxT(
", " );
462 matchingSymbolCandidates += candidateRef;
469 wxLogTrace(
traceTopoMatchDetail, wxT(
"Tie candidate symbol UUIDs: %s" ), candidateSymbolUuids );
473 if( symbolUuidHitCount == 1 )
478 std::rotate( aMatches.begin(), aMatches.begin() + uniqueMatchIdx, aMatches.begin() + uniqueMatchIdx + 1 );
486 else if( symbolUuidHitCount > 1 )
488 wxLogTrace(
traceTopoMatchDetail, wxT(
"Symbol UUID multiple matches (not usable) for %s: %s" ),
573 std::vector<TOPOLOGY_MISMATCH_REASON>& aMismatchReasons,
576 std::vector<BACKTRACK_STAGE> stack;
579 aMismatchReasons.clear();
585 int backtrackCount = 0;
586 double mrvTotalMs = 0.0;
588 std::vector<TOPOLOGY_MISMATCH_REASON> localReasons;
593 reason.
m_reason =
_(
"One or both of the areas has no components assigned." );
594 aMismatchReasons.push_back( reason );
601 reason.
m_reason =
_(
"Component count mismatch" );
602 aMismatchReasons.push_back( reason );
610 std::vector<std::vector<COMPONENT*>> structuralMatches( numRef );
615 std::vector<std::future<void>> futures;
616 futures.reserve( numRef );
618 const std::atomic<bool>* cancelled = aParams.
m_cancelled;
620 for(
size_t i = 0; i < numRef; i++ )
622 futures.emplace_back(
tp.submit_task(
623 [
this, i, aTarget, &structuralMatches, cancelled]()
625 if( cancelled && cancelled->load( std::memory_order_relaxed ) )
628 COMPONENT* ref = m_components[i];
629 TOPOLOGY_MISMATCH_REASON reason;
631 for( COMPONENT* tgt : aTarget->m_components )
633 if( ref->MatchesWith( tgt, reason ) )
634 structuralMatches[i].push_back( tgt );
639 for(
auto& f : futures )
642 timerPrecompute.
Stop();
645 wxT(
"Structural precomputation: %s (%d source x %d target)" ),
646 timerPrecompute.to_string(), (
int) numRef,
647 (
int) aTarget->m_components.size() );
649 if( aParams.m_cancelled && aParams.m_cancelled->load( std::memory_order_relaxed ) )
652 top.m_ref = m_components.front();
655 stack.push_back(
top );
657 bool matchFound =
false;
660 while( !stack.empty() )
662 if( aParams.m_cancelled && aParams.m_cancelled->load( std::memory_order_relaxed ) )
666 auto& current = stack.back();
668 for(
auto it = current.m_locked.begin(); it != current.m_locked.end(); it++ )
670 if (it->second == current.m_ref)
672 wxLogTrace(
traceTopoMatch, wxT(
"stk: Remove %s from locked\n" ),
673 current.m_ref->m_reference );
674 current.m_locked.erase( it );
679 if( nloops >= c_ITER_LIMIT )
684 reason.
m_reason =
_(
"Iteration count exceeded (timeout)" );
686 if( aMismatchReasons.empty() )
687 aMismatchReasons.push_back( reason );
689 aMismatchReasons.insert( aMismatchReasons.begin(), reason );
694 if( current.m_currentMatch < 0 )
696 PROF_TIMER timerInitMatch;
698 localReasons.clear();
699 current.m_matches = aTarget->findMatchingComponents(
700 current.m_ref, structuralMatches[current.m_refIndex],
701 current, localReasons, aParams.m_cancelled );
703 timerInitMatch.
Stop();
706 wxT(
"iter %d: initial match for '%s' (%d pins): %s, %d candidates" ),
707 nloops, current.m_ref->m_reference, current.m_ref->GetPinCount(),
708 timerInitMatch.
to_string(), (
int) current.m_matches.size() );
710 if( current.m_matches.empty() && aMismatchReasons.empty() && !localReasons.empty() )
711 aMismatchReasons = localReasons;
713 current.m_currentMatch = 0;
716 wxLogTrace(
traceTopoMatch, wxT(
"stk: Current '%s' stack %d cm %d/%d locked %d/%d\n" ),
717 current.m_ref->m_reference, (
int) stack.size(), current.m_currentMatch,
718 (
int) current.m_matches.size(), (
int) current.m_locked.size(),
719 (
int) m_components.size() );
721 if( current.m_currentMatch == 0 && current.m_matches.size() > 1 )
722 breakTie( current.m_ref, current.m_matches );
724 if ( current.m_matches.empty() )
726 wxLogTrace(
traceTopoMatch, wxT(
"stk: No matches at all, going up [level=%d]\n" ),
727 (
int) stack.size() );
733 if( current.m_currentMatch >= 0 && current.m_currentMatch >= current.m_matches.size() )
735 wxLogTrace(
traceTopoMatch, wxT(
"stk: No more matches, going up [level=%d]\n" ),
736 (
int) stack.size() );
742 auto& match = current.m_matches[current.m_currentMatch];
744 wxLogTrace(
traceTopoMatch, wxT(
"stk: candidate '%s', match list : ( " ),
745 current.m_matches[current.m_currentMatch]->m_reference, current.m_refIndex );
747 for(
auto m : current.m_matches )
748 wxLogTrace(
traceTopoMatch, wxT(
"%s " ), m->GetParent()->GetReferenceAsString() );
752 current.m_currentMatch++;
753 current.m_locked[match] = current.m_ref;
755 if( aParams.m_matchedComponents )
757 aParams.m_matchedComponents->store( (
int) current.m_locked.size(),
758 std::memory_order_relaxed );
761 if( current.m_locked.size() == m_components.size() )
763 current.m_nloops = nloops;
766 aMismatchReasons.clear();
768 for(
auto iter : current.m_locked )
769 aResult[ iter.second->GetParent() ] = iter.first->GetParent();
773 wxT(
"Isomorphism: %s, %d iterations, %d backtracks, "
774 "MRV total %0.1f ms (%d candidates)" ),
775 timerTotal.to_string(), nloops, backtrackCount, mrvTotalMs,
776 (
int) m_components.size() );
789 std::vector<COMPONENT*> m_matches;
790 std::vector<TOPOLOGY_MISMATCH_REASON> m_reasons;
793 std::vector<MRV_CANDIDATE> mrvCandidates;
797 std::unordered_set<COMPONENT*> lockedRefs;
798 lockedRefs.reserve( current.m_locked.size() );
800 for(
const auto& [tgt, ref] : current.m_locked )
801 lockedRefs.insert( ref );
803 for(
size_t i = 0; i < m_components.size(); i++ )
807 if( cmp != current.m_ref && lockedRefs.find( cmp ) == lockedRefs.end() )
808 mrvCandidates.push_back( { cmp, i, {}, {} } );
811 static const size_t MRV_PARALLEL_THRESHOLD = 4;
815 if( mrvCandidates.size() >= MRV_PARALLEL_THRESHOLD )
818 std::vector<std::future<void>> futures;
819 futures.reserve( mrvCandidates.size() );
821 const std::atomic<bool>* cancelled = aParams.m_cancelled;
823 for( MRV_CANDIDATE& c : mrvCandidates )
825 futures.emplace_back(
tp.submit_task(
826 [&c, aTarget, ¤t, &structuralMatches, cancelled]()
828 c.m_matches = aTarget->findMatchingComponents(
829 c.m_cmp, structuralMatches[c.m_index],
830 current, c.m_reasons, cancelled );
834 for(
auto& f : futures )
839 for( MRV_CANDIDATE& c : mrvCandidates )
841 c.m_matches = aTarget->findMatchingComponents(
842 c.m_cmp, structuralMatches[c.m_index],
843 current, c.m_reasons, aParams.m_cancelled );
848 double mrvMs = timerMrv.
msecs();
852 wxT(
"iter %d: MRV scan %0.3f ms, %d unlocked candidates" ),
853 nloops, mrvMs, (
int) mrvCandidates.size() );
855 if( aParams.m_cancelled && aParams.m_cancelled->load( std::memory_order_relaxed ) )
858 int minMatches = std::numeric_limits<int>::max();
861 int bestRefIndex = 0;
863 std::vector<COMPONENT*> bestMatches;
865 for( MRV_CANDIDATE& c : mrvCandidates )
867 int nMatches =
static_cast<int>( c.m_matches.size() );
871 bestNextRef = c.m_cmp;
872 bestRefIndex =
static_cast<int>( c.m_index );
873 bestMatches = std::move( c.m_matches );
876 else if( nMatches == 0 )
878 altNextRef = c.m_cmp;
879 altRefIndex =
static_cast<int>( c.m_index );
881 if( aMismatchReasons.empty() && !c.m_reasons.empty() )
882 aMismatchReasons = c.m_reasons;
884 else if( nMatches < minMatches )
886 minMatches = nMatches;
887 bestNextRef = c.m_cmp;
888 bestRefIndex =
static_cast<int>( c.m_index );
889 bestMatches = std::move( c.m_matches );
898 wxT(
"iter %d: MRV picked '%s' (%d matches, best of %d)" ),
900 (
int) bestMatches.size(), (
int) mrvCandidates.size() );
902 next.m_ref = bestNextRef;
903 next.m_refIndex = bestRefIndex;
904 next.m_matches = std::move( bestMatches );
905 next.m_currentMatch = 0;
910 wxT(
"iter %d: MRV dead end, alt='%s'" ),
911 nloops, altNextRef ? altNextRef->
m_reference : wxString(
"(none)" ) );
913 next.m_ref = altNextRef;
914 next.m_refIndex = altRefIndex;
915 next.m_currentMatch = -1;
918 stack.push_back(
next );
923 wxT(
"Isomorphism: %s, %d iterations, %d backtracks, "
924 "MRV total %0.1f ms (%d candidates)" ),
925 timerTotal.to_string(), nloops, backtrackCount, mrvTotalMs,
926 (
int) m_components.size() );