KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
topo_match.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <cstdio>
25#include <cstdlib>
26#include <cmath>
27#include <string>
28#include <vector>
29#include <algorithm>
30#include <cassert>
31#include <map>
32#include <set>
33#include <cctype>
34
35#include <pad.h>
36#include <footprint.h>
37#include <refdes_utils.h>
38#include <wx/string.h>
39#include <wx/log.h>
40
41#include "topo_match.h"
42
43
44static const wxString traceTopoMatch = wxT( "TOPO_MATCH" );
45
46namespace TMATCH
47{
48
49bool PIN::IsIsomorphic( const PIN& b ) const
50{
51 if( m_conns.size() != b.m_conns.size() )
52 {
53 wxLogTrace( traceTopoMatch,
54 wxT( "[conns mismatch n1 %d n2 %d c-ref %d c-other %d thispin %s-%s "
55 "otherpin %s-%s" ),
57 b.m_netcode,
58 (int) m_conns.size(),
59 (int) b.m_conns.size(),
61 m_ref,
63 b.m_ref );
64
65 for( auto c : m_conns )
66 {
67 wxLogTrace( traceTopoMatch, wxT( "%s-%s " ), c->m_parent->m_reference, c->m_ref );
68 }
69
70 wxLogTrace( traceTopoMatch, wxT( "||" ) );
71
72 for( auto c : b.m_conns )
73 {
74 wxLogTrace( traceTopoMatch, wxT( "%s-%s " ), c->m_parent->m_reference, c->m_ref );
75 }
76
77
78 wxLogTrace( traceTopoMatch, wxT( "] " ) );
79 return false;
80 }
81
82 if( m_conns.empty() )
83 {
84 wxLogTrace( traceTopoMatch, wxT( "[conns empty]" ) );
85 return true;
86 }
87
88 std::vector<bool> matches( m_conns.size() );
89
90 for( int i = 0; i < m_conns.size(); i++ )
91 matches[i] = false;
92
93 int nref = 0;
94
95 for( auto& cref : m_conns )
96 {
97 for( int i = 0; i < m_conns.size(); i++ )
98 {
99 if( b.m_conns[i]->IsTopologicallySimilar( *cref ) )
100 {
101 matches[nref] = true;
102 break;
103 }
104 }
105
106 nref++;
107 }
108
109 for( int i = 0; i < m_conns.size(); i++ )
110 {
111 if( !matches[i] )
112 {
113 return false;
114 }
115 }
116
117 return true;
118}
119
120
121// fixme: terrible performance, but computers are fast these days, ain't they? :D
122bool checkIfPadNetsMatch( const BACKTRACK_STAGE& aMatches, CONNECTION_GRAPH* aRefGraph,
123 COMPONENT* aRef, COMPONENT* aTgt )
124{
125 std::map<PIN*, PIN*> pairs;
126 std::vector<PIN*> pref, ptgt;
127
128 // GetMatchingComponentPairs() returns target->reference map
129 for( auto& m : aMatches.GetMatchingComponentPairs() )
130 {
131 for( PIN* p : m.second->Pins() )
132 {
133 pref.push_back( p );
134 }
135
136 for( PIN* p : m.first->Pins() )
137 {
138 ptgt.push_back( p );
139 }
140 }
141
142 for( PIN* p : aRef->Pins() )
143 {
144 pref.push_back( p );
145 }
146
147 for( PIN* p : aTgt->Pins() )
148 {
149 ptgt.push_back( p );
150 }
151
152 if( pref.size() != ptgt.size() )
153 {
154 return false;
155 }
156
157 for( unsigned int i = 0; i < pref.size(); i++ )
158 {
159 pairs[pref[i]] = ptgt[i];
160 }
161
162 for( PIN* refPin : aRef->Pins() )
163 {
164 wxLogTrace( traceTopoMatch, wxT( "pad %s-%s: " ),
165 aRef->GetParent()->GetReferenceAsString(), refPin->GetReference() );
166
167 std::optional<int> prevNet;
168
169 for( COMPONENT* refCmp : aRefGraph->Components() )
170 {
171 for( PIN* ppin : refCmp->Pins() )
172 {
173 if ( ppin->GetNetCode() != refPin->GetNetCode() )
174 continue;
175
176 wxLogTrace( traceTopoMatch, wxT( "{ref %s-%s:%d} " ),
177 ppin->GetParent()->GetParent()->GetReferenceAsString(),
178 ppin->GetReference(), ppin->GetNetCode() );
179
180 auto tpin = pairs.find( ppin );
181
182 if( tpin != pairs.end() )
183 {
184 int nc = tpin->second->GetNetCode();
185
186 if( prevNet && ( *prevNet != nc ) )
187 {
188 wxLogTrace( traceTopoMatch, wxT( "nets inconsistent\n" ) );
189 return false;
190 }
191
192 prevNet = nc;
193 }
194 }
195 }
196 }
197
198 return true;
199}
200
201
202std::vector<COMPONENT*>
204 const BACKTRACK_STAGE& partialMatches )
205{
206 std::vector<COMPONENT*> matches;
207 for( auto cmpTarget : m_components )
208 {
209 // already matched to sth? move on.
210 if( partialMatches.m_locked.find( cmpTarget ) != partialMatches.m_locked.end() )
211 {
212 continue;
213 }
214
215 wxLogTrace( traceTopoMatch, wxT( "Check '%s'/'%s' " ), aRef->m_reference,
216 cmpTarget->m_reference );
217
218 // first, a basic heuristic (reference prefix, pin count & footprint) followed by a pin
219 // connection topology check
220 if( aRef->MatchesWith( cmpTarget ) )
221 {
222 // then a net integrity check (expensive because of poor optimization)
223 if( checkIfPadNetsMatch( partialMatches, aRefGraph, aRef, cmpTarget ) )
224 {
225 wxLogTrace( traceTopoMatch, wxT("match!\n") );
226 matches.push_back( cmpTarget );
227 }
228 else
229 {
230 wxLogTrace( traceTopoMatch, wxT("Reject [net topo mismatch]\n") );
231 }
232 }
233 else
234 {
235 wxLogTrace( traceTopoMatch, wxT("reject\n") );
236 }
237 }
238
239 auto padSimilarity=[]( COMPONENT*a, COMPONENT*b ) -> double
240 {
241 int n=0;
242
243 for(int i=0;i<a->m_pins.size();i++)
244 {
245 PIN* pa = a->m_pins[i];
246 PIN* pb = b->m_pins[i];
247
248 if( pa->GetNetCode() == pb->GetNetCode() )
249 n++;
250 }
251
252 return (double)n / (double) a->m_pins.size();
253 };
254
255
256 std::sort(matches.begin(), matches.end(), [&] ( COMPONENT*a, COMPONENT*b ) -> int
257 {
258 return padSimilarity( aRef,a ) > padSimilarity( aRef, b );
259 }
260);
261
262 return matches;
263}
264
265
267{
268 std::sort( m_pins.begin(), m_pins.end(),
269 []( PIN* a, PIN* b )
270 {
271 return a->GetReference() < b->GetReference();
272 } );
273}
274
275
277{
278 std::map<int, std::vector<PIN*>> nets;
279
281
282 for( auto c : m_components )
283 {
284 c->sortPinsByName();
285
286 for( auto p : c->Pins() )
287 {
288 if( p->GetNetCode() > 0 )
289 nets[p->GetNetCode()].push_back( p );
290 }
291 }
292
293 for( auto iter : nets )
294 {
295 wxLogTrace( traceTopoMatch, wxT( "net %d: %d connections\n" ), iter.first,
296 (int) iter.second.size() );
297
298 for( auto p : iter.second )
299 {
300 for( auto p2 : iter.second )
301 {
302 if( p != p2 && !alg::contains( p->m_conns, p2 ) )
303 {
304 p->m_conns.push_back( p2 );
305 }
306 }
307 }
308 }
309
310/* for( auto c : m_components )
311 for( auto p : c->Pins() )
312 {
313 printf("pin %s: \n", p->m_ref.c_str().AsChar() );
314
315 for( auto c : p->m_conns )
316 printf( "%s ", c->m_ref.c_str().AsChar() );
317 printf("\n");
318 }
319 */
320}
321
322
324 COMPONENT_MATCHES& aResult )
325{
326 std::vector<BACKTRACK_STAGE> stack;
327 BACKTRACK_STAGE top;
328
329 if( m_components.empty()|| aTarget->m_components.empty() )
330 return ST_EMPTY;
331
332 if( m_components.size() != aTarget->m_components.size() )
334
335 top.m_ref = m_components.front();
336 top.m_refIndex = 0;
337
338 stack.push_back( top );
339
340 bool matchFound = false;
341 int nloops = 0;
342
343 while( !stack.empty() )
344 {
345 nloops++;
346 auto& current = stack.back();
347
348 for( auto it = current.m_locked.begin(); it != current.m_locked.end(); it++ )
349 {
350 if (it->second == current.m_ref)
351 {
352 wxLogTrace( traceTopoMatch, wxT( "stk: Remove %s from locked\n" ),
353 current.m_ref->m_reference );
354 current.m_locked.erase( it );
355 break;
356 }
357 }
358
359 if( nloops >= c_ITER_LIMIT )
360 {
361 wxLogTrace( traceTopoMatch, wxT( "stk: Iter cnt exceeded\n" ) );
363 }
364
365 if( current.m_currentMatch < 0 )
366 {
367 current.m_matches = aTarget->findMatchingComponents( this, current.m_ref, current );
368 current.m_currentMatch = 0;
369 }
370
371 wxLogTrace( traceTopoMatch, wxT( "stk: Current '%s' stack %d cm %d/%d locked %d/%d\n" ),
372 current.m_ref->m_reference, (int) stack.size(), current.m_currentMatch,
373 (int) current.m_matches.size(), (int) current.m_locked.size(),
374 (int) m_components.size() );
375
376 if ( current.m_matches.empty() )
377 {
378 wxLogTrace( traceTopoMatch, wxT( "stk: No matches at all, going up [level=%d]\n" ),
379 (int) stack.size() );
380 stack.pop_back();
381 continue;
382 }
383
384 if( current.m_currentMatch >= 0 && current.m_currentMatch >= current.m_matches.size() )
385 {
386 wxLogTrace( traceTopoMatch, wxT( "stk: No more matches, going up [level=%d]\n" ),
387 (int) stack.size() );
388 stack.pop_back();
389 continue;
390 }
391
392 auto& match = current.m_matches[current.m_currentMatch];
393
394 wxLogTrace( traceTopoMatch, wxT( "stk: candidate '%s', match list : ( " ),
395 current.m_matches[current.m_currentMatch]->m_reference, current.m_refIndex );
396
397 for( auto m : current.m_matches )
398 wxLogTrace( traceTopoMatch, wxT( "%s " ), m->GetParent()->GetReferenceAsString() );
399
400 wxLogTrace( traceTopoMatch, wxT( "\n" ) );
401
402 current.m_currentMatch++;
403 current.m_locked[match] = current.m_ref;
404
405 if( current.m_locked.size() == m_components.size() )
406 {
407 current.m_nloops = nloops;
408
409 aResult.clear();
410
411 for( auto iter : current.m_locked )
412 aResult[ iter.second->GetParent() ] = iter.first->GetParent();
413
414 return ST_OK;
415 }
416
417
418 int minMatches = std::numeric_limits<int>::max();
419 COMPONENT* altNextRef = nullptr;
420 COMPONENT* bestNextRef = nullptr;
421 int bestRefIndex = 0;
422 int altRefIndex = 0;
423
424 for( size_t i = 0; i < m_components.size(); i++ )
425 {
426 COMPONENT* cmp = m_components[i];
427
428 if( cmp == current.m_ref )
429 continue;
430
431 bool found = false;
432
433 for( auto it = current.m_locked.begin(); it != current.m_locked.end(); it++ )
434 {
435 if( it->second == cmp )
436 {
437 found = true;
438 break;
439 }
440 }
441
442 if( found )
443 continue;
444
445 auto matches = aTarget->findMatchingComponents( this, cmp, current );
446
447 int nMatches = matches.size();
448
449 if( nMatches == 1 )
450 {
451 bestNextRef = cmp;
452 bestRefIndex = i;
453 break;
454 }
455 else if( nMatches == 0 )
456 {
457 altNextRef = cmp;
458 altRefIndex = i;
459 }
460 else if( nMatches < minMatches )
461 {
462 minMatches = nMatches;
463 bestNextRef = cmp;
464 bestRefIndex = i;
465 }
466 }
467
468 BACKTRACK_STAGE next( current );
469 next.m_currentMatch = -1;
470
471 if( bestNextRef )
472 {
473 next.m_ref = bestNextRef;
474 next.m_refIndex = bestRefIndex;
475 }
476 else
477 {
478 next.m_ref = altNextRef;
479 next.m_refIndex = altRefIndex;
480 }
481
482 stack.push_back( next );
483 };
484
485
487}
488
489
490#if 0
491int main()
492{
493 FILE * f = fopen("connectivity.dump","rb" );
494 auto cgRef = loadCGraph(f);
495 auto cgTarget = loadCGraph(f);
496
497 cgRef->buildConnectivity();
498 cgTarget->buildConnectivity();
499
500 int attempts = 0;
501 int max_loops = 0;
502
503 for( ;; )
504 {
505 cgRef->shuffle();
506 cgTarget->shuffle();
507
508 const BacktrackStage latest = cgRef->matchCGraphs( cgTarget );
509
510 if( !latest.locked.size() )
511 {
512 printf("MATCH FAIL\n");
513 break;
514 }
515
516 //printf("loops: %d\n", latest.nloops );
517 //printf("Locked: %d\n", latest.locked.size() );
518
519 //if (matchFound)
520 //{
521 // for( auto& iter : latest.locked )
522 //{
523 // printf("%-10s : %-10s\n", iter.first->reference.c_str(), iter.second->reference.c_str() );
524 //}
525
526 //}
527
528 if( latest.nloops > max_loops )
529 {
530 max_loops = latest.nloops;
531 }
532
533 if (attempts % 10000 == 0)
534 {
535 printf("attempts: %d maxloops: %d\n", attempts, max_loops );
536 }
537
538 attempts++;
539
540 }
541
542 fclose(f);
543
544 return 0;
545}
546
547#endif
548
549
550COMPONENT::COMPONENT( const wxString& aRef, FOOTPRINT* aParentFp,
551 std::optional<VECTOR2I> aRaOffset ) :
552 m_reference( aRef ),
553 m_parentFootprint( aParentFp ), m_raOffset( aRaOffset )
554{
556}
557
558
559bool COMPONENT::IsSameKind( const COMPONENT& b ) const
560{
561 return m_prefix == b.m_prefix
564 && b.m_parentFootprint->GetFPID().empty() ) );
565}
566
567
569{
570 m_pins.push_back( aPin );
571 aPin->SetParent( this );
572}
573
574
576{
577 if( GetPinCount() != b->GetPinCount() )
578 {
579 return false;
580 }
581
582 if( !IsSameKind( *b ) )
583 {
584 return false;
585 }
586
587 for( int pin = 0; pin < b->GetPinCount(); pin++ )
588 {
589 if( !b->m_pins[pin]->IsIsomorphic( *m_pins[pin] ) )
590 {
591 return false;
592 }
593
594 }
595
596 return true;
597}
598
599
601{
602 auto cmp = new COMPONENT( aFp->GetReference(), aFp );
603
604 for( auto pad : aFp->Pads() )
605 {
606 auto pin = new PIN( );
607 pin->m_netcode = pad->GetNetCode();
608 pin->m_ref = pad->GetNumber();
609 cmp->AddPin( pin );
610 }
611
612 m_components.push_back( cmp );
613}
614
615
616std::unique_ptr<CONNECTION_GRAPH>
617CONNECTION_GRAPH::BuildFromFootprintSet( const std::set<FOOTPRINT*>& aFps )
618{
619 auto cgraph = std::make_unique<CONNECTION_GRAPH>();
620 VECTOR2I ref(0, 0);
621
622 if( aFps.size() > 0 )
623 ref = (*aFps.begin())->GetPosition();
624
625 for( auto fp : aFps )
626 {
627 cgraph->AddFootprint( fp, fp->GetPosition() - ref );
628 }
629
630 cgraph->BuildConnectivity();
631
632 return std::move(cgraph);
633}
634
635
637{
638
639}
640
641
643{
644 for( COMPONENT* fp : m_components )
645 {
646 delete fp;
647 }
648}
649
650
652{
653 for( PIN* p : m_pins )
654 {
655 delete p;
656 }
657}
658
659
660}; // namespace TMATCH
std::deque< PAD * > & Pads()
Definition: footprint.h:211
const LIB_ID & GetFPID() const
Definition: footprint.h:253
wxString GetReferenceAsString() const
Definition: footprint.h:630
const wxString & GetReference() const
Definition: footprint.h:621
bool empty() const
Definition: lib_id.h:193
const std::map< COMPONENT *, COMPONENT * > & GetMatchingComponentPairs() const
Definition: topo_match.h:138
std::map< COMPONENT *, COMPONENT * > m_locked
Definition: topo_match.h:145
bool MatchesWith(COMPONENT *b)
Definition: topo_match.cpp:575
bool IsSameKind(const COMPONENT &b) const
Definition: topo_match.cpp:559
int GetPinCount() const
Definition: topo_match.h:56
COMPONENT(const wxString &aRef, FOOTPRINT *aParentFp, std::optional< VECTOR2I > aRaOffset=std::optional< VECTOR2I >())
Definition: topo_match.cpp:550
FOOTPRINT * m_parentFootprint
Definition: topo_match.h:71
wxString m_prefix
Definition: topo_match.h:70
void AddPin(PIN *p)
Definition: topo_match.cpp:568
std::vector< PIN * > & Pins()
Definition: topo_match.h:58
wxString m_reference
Definition: topo_match.h:69
std::vector< PIN * > m_pins
Definition: topo_match.h:72
FOOTPRINT * GetParent() const
Definition: topo_match.h:59
STATUS FindIsomorphism(CONNECTION_GRAPH *target, COMPONENT_MATCHES &result)
Definition: topo_match.cpp:323
std::vector< COMPONENT * > findMatchingComponents(CONNECTION_GRAPH *aRefGraph, COMPONENT *ref, const BACKTRACK_STAGE &partialMatches)
Definition: topo_match.cpp:203
std::vector< COMPONENT * > & Components()
Definition: topo_match.h:172
std::vector< COMPONENT * > m_components
Definition: topo_match.h:189
void AddFootprint(FOOTPRINT *aFp, const VECTOR2I &aOffset)
Definition: topo_match.cpp:600
static std::unique_ptr< CONNECTION_GRAPH > BuildFromFootprintSet(const std::set< FOOTPRINT * > &aFps)
Definition: topo_match.cpp:617
std::vector< PIN * > m_conns
Definition: topo_match.h:112
void SetParent(COMPONENT *parent)
Definition: topo_match.h:83
COMPONENT * m_parent
Definition: topo_match.h:111
bool IsIsomorphic(const PIN &b) const
Definition: topo_match.cpp:49
int GetNetCode() const
Definition: topo_match.h:101
wxString m_ref
Definition: topo_match.h:109
main()
std::map< FOOTPRINT *, FOOTPRINT * > COMPONENT_MATCHES
Definition: topo_match.h:149
bool checkIfPadNetsMatch(const BACKTRACK_STAGE &aMatches, CONNECTION_GRAPH *aRefGraph, COMPONENT *aRef, COMPONENT *aTgt)
Definition: topo_match.cpp:122
wxString GetRefDesPrefix(const wxString &aRefDes)
Get the (non-numeric) prefix from a refdes - e.g.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
CITER next(CITER it)
Definition: ptree.cpp:124
Collection of utility functions for component reference designators (refdes)
static const wxString traceTopoMatch
Definition: topo_match.cpp:44