262 const std::vector<COMPONENT*>& aStructuralMatches,
265 std::vector<TOPOLOGY_MISMATCH_REASON>& aMismatchReasons,
266 const std::atomic<bool>* aCancelled )
268 if( aCancelled && aCancelled->load( std::memory_order_relaxed ) )
273 aMismatchReasons.clear();
274 std::vector<COMPONENT*> matches;
275 int candidatesChecked = 0;
281 double netCheckMs = 0.0;
283 for(
COMPONENT* cmpTarget : aStructuralMatches )
285 if( partialMatches.
m_locked.find( cmpTarget ) != partialMatches.
m_locked.end() )
291 cmpTarget->m_reference );
295 localReason.
m_candidate = cmpTarget->GetParent()->GetReferenceAsString();
301 netCheckMs += timerNet.
msecs();
306 matches.push_back( cmpTarget );
310 wxLogTrace(
traceTopoMatch, wxT(
"Reject [net topo mismatch]\n" ) );
311 aMismatchReasons.push_back( localReason );
317 std::unordered_map<COMPONENT*, double> simScores;
318 simScores.reserve( matches.size() );
324 for(
size_t i = 0; i < aRef->
m_pins.size(); i++ )
326 if( aRef->
m_pins[i]->GetNetCode() == match->m_pins[i]->GetNetCode() )
330 simScores[match] =
static_cast<double>( n ) /
static_cast<double>( aRef->
m_pins.size() );
333 std::sort( matches.begin(), matches.end(),
336 double simA = simScores[a];
337 double simB = simScores[b];
342 return a->GetParent()->GetReferenceAsString()
343 < b->GetParent()->GetReferenceAsString();
348 if( matches.empty() && aMismatchReasons.empty() )
355 if( !aStructuralReason.
m_reason.IsEmpty() )
357 aMismatchReasons.push_back( aStructuralReason );
363 reason.
m_reason =
_(
"No compatible component found in the target area." );
364 aMismatchReasons.push_back( reason );
371 wxT(
" findMatch '%s' (%d pins): %s total, checked %d/%d structural, "
372 "netCheck %0.3f ms, score %0.3f ms, %d matches" ),
374 candidatesChecked, (
int) aStructuralMatches.size(),
375 netCheckMs, timerScore.
msecs(),
376 (
int) matches.size() );
460 auto getSymbolInstanceUuid =
471 const KIID& symbolUuid =
path.back();
477 const KIID refSymbolUuid = getSymbolInstanceUuid( refFp );
478 wxString candidateSymbolUuids;
479 wxString matchingSymbolCandidates;
480 int symbolUuidHitCount = 0;
481 int uniqueMatchIdx = -1;
493 for(
size_t i = 0; i < aMatches.size(); i++ )
495 FOOTPRINT* candidateFp = aMatches[i]->GetParent();
497 const KIID candidateSymbolUuid = getSymbolInstanceUuid( candidateFp );
500 candidateSymbolUuids += wxT(
", " );
502 if( candidateSymbolUuid ==
niluuid )
503 candidateSymbolUuids += candidateRef + wxT(
"=<none>" );
505 candidateSymbolUuids += candidateRef + wxT(
"=" ) + candidateSymbolUuid.
AsString();
507 if( candidateSymbolUuid == refSymbolUuid )
509 if( uniqueMatchIdx < 0 )
510 uniqueMatchIdx =
static_cast<int>( i );
512 symbolUuidHitCount++;
514 if( !matchingSymbolCandidates.IsEmpty() )
515 matchingSymbolCandidates += wxT(
", " );
517 matchingSymbolCandidates += candidateRef;
524 wxLogTrace(
traceTopoMatchDetail, wxT(
"Tie candidate symbol UUIDs: %s" ), candidateSymbolUuids );
528 if( symbolUuidHitCount == 1 )
533 std::rotate( aMatches.begin(), aMatches.begin() + uniqueMatchIdx, aMatches.begin() + uniqueMatchIdx + 1 );
541 else if( symbolUuidHitCount > 1 )
543 wxLogTrace(
traceTopoMatchDetail, wxT(
"Symbol UUID multiple matches (not usable) for %s: %s" ),
637 std::vector<TOPOLOGY_MISMATCH_REASON>& aMismatchReasons,
640 std::vector<BACKTRACK_STAGE> stack;
643 aMismatchReasons.clear();
649 int backtrackCount = 0;
650 double mrvTotalMs = 0.0;
652 std::vector<TOPOLOGY_MISMATCH_REASON> localReasons;
657 reason.
m_reason =
_(
"One or both of the areas has no components assigned." );
658 aMismatchReasons.push_back( reason );
665 reason.
m_reason =
_(
"Component count mismatch" );
666 aMismatchReasons.push_back( reason );
674 std::vector<std::vector<COMPONENT*>> structuralMatches( numRef );
679 std::vector<TOPOLOGY_MISMATCH_REASON> structuralReasons( numRef );
684 std::vector<std::future<void>> futures;
685 futures.reserve( numRef );
687 const std::atomic<bool>* cancelled = aParams.
m_cancelled;
689 for(
size_t i = 0; i < numRef; i++ )
691 futures.emplace_back(
tp.submit_task(
692 [
this, i, aTarget, &structuralMatches, &structuralReasons, cancelled]()
694 if( cancelled && cancelled->load( std::memory_order_relaxed ) )
697 COMPONENT* ref = m_components[i];
698 TOPOLOGY_MISMATCH_REASON reason;
699 TOPOLOGY_MISMATCH_REASON bestReason;
701 for( COMPONENT* tgt : aTarget->m_components )
703 if( ref->MatchesWith( tgt, reason ) )
705 structuralMatches[i].push_back( tgt );
707 else if( bestReason.m_reason.IsEmpty() || ref->IsSameKind( *tgt ) )
716 if( structuralMatches[i].empty() )
717 structuralReasons[i] = bestReason;
721 for(
auto& f : futures )
724 timerPrecompute.
Stop();
727 wxT(
"Structural precomputation: %s (%d source x %d target)" ),
728 timerPrecompute.to_string(), (
int) numRef,
729 (
int) aTarget->m_components.size() );
731 if( aParams.m_cancelled && aParams.m_cancelled->load( std::memory_order_relaxed ) )
734 top.m_ref = m_components.front();
737 stack.push_back(
top );
741 while( !stack.empty() )
743 if( aParams.m_cancelled && aParams.m_cancelled->load( std::memory_order_relaxed ) )
747 auto& current = stack.back();
749 for(
auto it = current.m_locked.begin(); it != current.m_locked.end(); it++ )
751 if (it->second == current.m_ref)
753 wxLogTrace(
traceTopoMatch, wxT(
"stk: Remove %s from locked\n" ),
754 current.m_ref->m_reference );
755 current.m_locked.erase( it );
760 if( nloops >= c_ITER_LIMIT )
765 reason.
m_reason =
_(
"Iteration count exceeded (timeout)" );
767 if( aMismatchReasons.empty() )
768 aMismatchReasons.push_back( reason );
770 aMismatchReasons.insert( aMismatchReasons.begin(), reason );
775 if( current.m_currentMatch < 0 )
777 PROF_TIMER timerInitMatch;
779 localReasons.clear();
780 current.m_matches = aTarget->findMatchingComponents(
781 current.m_ref, structuralMatches[current.m_refIndex],
782 structuralReasons[current.m_refIndex], current, localReasons,
783 aParams.m_cancelled );
785 timerInitMatch.
Stop();
788 wxT(
"iter %d: initial match for '%s' (%d pins): %s, %d candidates" ),
789 nloops, current.m_ref->m_reference, current.m_ref->GetPinCount(),
790 timerInitMatch.
to_string(), (
int) current.m_matches.size() );
792 if( current.m_matches.empty() && aMismatchReasons.empty() && !localReasons.empty() )
793 aMismatchReasons = localReasons;
795 current.m_currentMatch = 0;
798 wxLogTrace(
traceTopoMatch, wxT(
"stk: Current '%s' stack %d cm %d/%d locked %d/%d\n" ),
799 current.m_ref->m_reference, (
int) stack.size(), current.m_currentMatch,
800 (
int) current.m_matches.size(), (
int) current.m_locked.size(),
801 (
int) m_components.size() );
803 if( current.m_currentMatch == 0 && current.m_matches.size() > 1 )
804 breakTie( current.m_ref, current.m_matches );
806 if ( current.m_matches.empty() )
808 wxLogTrace(
traceTopoMatch, wxT(
"stk: No matches at all, going up [level=%d]\n" ),
809 (
int) stack.size() );
815 if( current.m_currentMatch >= 0
816 &&
static_cast<size_t>( current.m_currentMatch ) >= current.m_matches.size() )
818 wxLogTrace(
traceTopoMatch, wxT(
"stk: No more matches, going up [level=%d]\n" ),
819 (
int) stack.size() );
825 auto& match = current.m_matches[current.m_currentMatch];
827 wxLogTrace(
traceTopoMatch, wxT(
"stk: candidate '%s', match list : ( " ),
828 current.m_matches[current.m_currentMatch]->m_reference, current.m_refIndex );
830 for(
auto m : current.m_matches )
831 wxLogTrace(
traceTopoMatch, wxT(
"%s " ), m->GetParent()->GetReferenceAsString() );
835 current.m_currentMatch++;
836 current.m_locked[match] = current.m_ref;
838 if( aParams.m_matchedComponents )
840 aParams.m_matchedComponents->store( (
int) current.m_locked.size(),
841 std::memory_order_relaxed );
844 if( current.m_locked.size() == m_components.size() )
846 current.m_nloops = nloops;
849 aMismatchReasons.clear();
851 for(
auto iter : current.m_locked )
852 aResult[ iter.second->GetParent() ] = iter.first->GetParent();
856 wxT(
"Isomorphism: %s, %d iterations, %d backtracks, "
857 "MRV total %0.1f ms (%d candidates)" ),
858 timerTotal.to_string(), nloops, backtrackCount, mrvTotalMs,
859 (
int) m_components.size() );
872 std::vector<COMPONENT*> m_matches;
873 std::vector<TOPOLOGY_MISMATCH_REASON> m_reasons;
876 std::vector<MRV_CANDIDATE> mrvCandidates;
880 std::unordered_set<COMPONENT*> lockedRefs;
881 lockedRefs.reserve( current.m_locked.size() );
883 for(
const auto& [tgt, ref] : current.m_locked )
884 lockedRefs.insert( ref );
886 for(
size_t i = 0; i < m_components.size(); i++ )
890 if( cmp != current.m_ref && lockedRefs.find( cmp ) == lockedRefs.end() )
891 mrvCandidates.push_back( { cmp, i, {}, {} } );
894 static const size_t MRV_PARALLEL_THRESHOLD = 4;
898 if( mrvCandidates.size() >= MRV_PARALLEL_THRESHOLD )
901 std::vector<std::future<void>> futures;
902 futures.reserve( mrvCandidates.size() );
904 const std::atomic<bool>* cancelled = aParams.m_cancelled;
906 for( MRV_CANDIDATE& c : mrvCandidates )
908 futures.emplace_back(
tp.submit_task(
909 [&c, aTarget, ¤t, &structuralMatches, &structuralReasons, cancelled]()
911 c.m_matches = aTarget->findMatchingComponents(
912 c.m_cmp, structuralMatches[c.m_index],
913 structuralReasons[c.m_index], current, c.m_reasons,
918 for(
auto& f : futures )
923 for( MRV_CANDIDATE& c : mrvCandidates )
925 c.m_matches = aTarget->findMatchingComponents(
926 c.m_cmp, structuralMatches[c.m_index],
927 structuralReasons[c.m_index], current, c.m_reasons,
928 aParams.m_cancelled );
933 double mrvMs = timerMrv.
msecs();
937 wxT(
"iter %d: MRV scan %0.3f ms, %d unlocked candidates" ),
938 nloops, mrvMs, (
int) mrvCandidates.size() );
940 if( aParams.m_cancelled && aParams.m_cancelled->load( std::memory_order_relaxed ) )
943 int minMatches = std::numeric_limits<int>::max();
946 int bestRefIndex = 0;
948 std::vector<COMPONENT*> bestMatches;
950 for( MRV_CANDIDATE& c : mrvCandidates )
952 int nMatches =
static_cast<int>( c.m_matches.size() );
956 bestNextRef = c.m_cmp;
957 bestRefIndex =
static_cast<int>( c.m_index );
958 bestMatches = std::move( c.m_matches );
961 else if( nMatches == 0 )
963 altNextRef = c.m_cmp;
964 altRefIndex =
static_cast<int>( c.m_index );
966 if( aMismatchReasons.empty() && !c.m_reasons.empty() )
967 aMismatchReasons = c.m_reasons;
969 else if( nMatches < minMatches )
971 minMatches = nMatches;
972 bestNextRef = c.m_cmp;
973 bestRefIndex =
static_cast<int>( c.m_index );
974 bestMatches = std::move( c.m_matches );
983 wxT(
"iter %d: MRV picked '%s' (%d matches, best of %d)" ),
985 (
int) bestMatches.size(), (
int) mrvCandidates.size() );
987 next.m_ref = bestNextRef;
988 next.m_refIndex = bestRefIndex;
989 next.m_matches = std::move( bestMatches );
990 next.m_currentMatch = 0;
995 wxT(
"iter %d: MRV dead end, alt='%s'" ),
996 nloops, altNextRef ? altNextRef->
m_reference : wxString(
"(none)" ) );
998 next.m_ref = altNextRef;
999 next.m_refIndex = altRefIndex;
1000 next.m_currentMatch = -1;
1003 stack.push_back(
next );
1008 wxT(
"Isomorphism: %s, %d iterations, %d backtracks, "
1009 "MRV total %0.1f ms (%d candidates)" ),
1010 timerTotal.to_string(), nloops, backtrackCount, mrvTotalMs,
1011 (
int) m_components.size() );