xmsmesh  1.0
MeWeightMatcher.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
6 //------------------------------------------------------------------------------
7 
8 // defines for debugging
9 //#define DEBUG
10 //#define _DEBUG 1
11 //#define CHECK_DELTA
12 //#define CHECK_OPTIMUM
13 #ifdef DEBUG
15 #endif
16 
17 //----- Included files ---------------------------------------------------------
18 
19 // 1. Precompiled header
20 
21 // 2. My own header
23 
24 // 3. Standard library headers
25 #include <numeric>
26 
27 // 4. External library headers
28 
29 // 5. Shared code headers
30 #include <xmscore/misc/XmError.h>
31 #include <xmscore/stl/vector.h>
32 
33 // 6. Non-shared code headers
34 
35 //----- Forward declarations ---------------------------------------------------
36 
37 //----- External globals -------------------------------------------------------
38 
39 //----- Namespace declaration --------------------------------------------------
40 
41 namespace xms
42 {
43 //----- Constants / Enumerations -----------------------------------------------
44 
45 //----- Classes / Structs ------------------------------------------------------
46 
47 //----- Internal functions -----------------------------------------------------
48 
49 //----- Class / Function definitions -------------------------------------------
50 
51 namespace
52 {
58 class VecIntPy
59 {
60 public:
64  explicit VecIntPy(int a_size = 0, int a_value = 0)
65  : m_vec(a_size, a_value)
66  {
67  }
68 
71  VecIntPy(const VecInt& a_vec)
72  : m_vec(a_vec)
73  {
74  }
75 
78  VecIntPy(const VecIntPy& a_vec)
79  : m_vec(a_vec.m_vec)
80  {
81  }
82 
85  const VecIntPy& operator=(const VecIntPy& a_rhs)
86  {
87  m_vec = a_rhs.m_vec;
88  return *this;
89  }
90 
95  int& operator[](int a_idx)
96  {
97  if (a_idx < 0)
98  {
99  a_idx = (int)m_vec.size() + a_idx;
100  }
101  XM_ASSERT(a_idx >= 0 && a_idx < m_vec.size());
102  return m_vec[a_idx];
103  }
104 
109  int operator[](int a_idx) const
110  {
111  if (a_idx < 0)
112  {
113  a_idx = (int)m_vec.size() + a_idx;
114  }
115  XM_ASSERT(a_idx >= 0 && a_idx < m_vec.size());
116  return m_vec[a_idx];
117  }
118 
121  const VecInt& ToVecInt() const { return m_vec; }
122 
125  int size() const { return (int)m_vec.size(); }
126 
129  bool empty() const { return m_vec.empty(); }
130 
134  int index(int a_value)
135  {
136  auto it = std::find(m_vec.begin(), m_vec.end(), a_value);
137  XM_ASSERT(it != m_vec.end());
138  return (int)(it - m_vec.begin());
139  }
140 
143  int min() const
144  {
145  auto it = std::min_element(m_vec.begin(), m_vec.end());
146  XM_ASSERT(it != m_vec.end());
147  return *it;
148  }
149 
155  int min(int a_start, int a_end) const
156  {
157  auto it = std::min_element(m_vec.begin() + a_start, m_vec.begin() + a_end);
158  XM_ASSERT(a_start < a_end);
159  XM_ASSERT(a_start >= 0 && a_end <= (int)m_vec.size());
160  XM_ASSERT(it != m_vec.begin() + a_end);
161  return *it;
162  }
163 
166  int max() const
167  {
168  auto it = std::max_element(m_vec.begin(), m_vec.end());
169  XM_ASSERT(it != m_vec.end());
170  return *it;
171  }
172 
178  int max(int a_start, int a_end) const
179  {
180  auto it = std::max_element(m_vec.begin() + a_start, m_vec.begin() + a_end);
181  XM_ASSERT(a_start < a_end);
182  XM_ASSERT(a_start >= 0 && a_end <= (int)m_vec.size());
183  XM_ASSERT(it != m_vec.begin() + a_end);
184  return *it;
185  }
186 
189  void push_back(int a_value) { m_vec.push_back(a_value); }
190 
193  int back()
194  {
195  XM_ASSERT(!m_vec.empty());
196  return m_vec.back();
197  }
198 
202  int pop()
203  {
204  XM_ASSERT(!m_vec.empty());
205  int back = m_vec.back();
206  m_vec.pop_back();
207  return back;
208  }
209 
212  void append(const VecIntPy& a_vec)
213  {
214  m_vec.insert(m_vec.end(), a_vec.m_vec.begin(), a_vec.m_vec.end());
215  }
216 
218  void clear() { m_vec.clear(); }
219 
225  void resize(int a_size, int a_default) { m_vec.resize(a_size, a_default); }
226 
229  void reserve(int a_size) { m_vec.reserve(a_size); }
230 
235  void resize_to_count(int a_size, int a_start_at = 0)
236  {
237  m_vec.resize(a_size);
238  std::iota(m_vec.begin(), m_vec.end(), a_start_at);
239  }
240 
242  void reverse() { std::reverse(m_vec.begin(), m_vec.end()); }
243 
246  void rotate(int a_frontIdx)
247  {
248  std::rotate(m_vec.begin(), m_vec.begin() + a_frontIdx, m_vec.end());
249  }
250 
251 private:
253 };
254 
260 class VecInt2dPy
261 {
262 public:
266  explicit VecInt2dPy(int a_size = 0, VecIntPy a_value = VecIntPy())
267  : m_vec(a_size, a_value)
268  {
269  }
270 
273  VecInt2dPy(const VecInt2dPy& a_vec)
274  : m_vec(a_vec.m_vec)
275  {
276  }
277 
280  VecInt2dPy(const VecInt2d& a_vec)
281  : m_vec()
282  {
283  for (auto& vec : a_vec)
284  {
285  m_vec.push_back(VecIntPy(vec));
286  }
287  }
288 
293  VecIntPy& operator[](int a_idx)
294  {
295  if (a_idx < 0)
296  {
297  a_idx = (int)m_vec.size() + a_idx;
298  }
299  XM_ASSERT(a_idx >= 0 && a_idx < m_vec.size());
300  return m_vec[a_idx];
301  }
302 
307  const VecIntPy& operator[](int a_idx) const
308  {
309  if (a_idx < 0)
310  {
311  a_idx = (int)m_vec.size() + a_idx;
312  }
313  XM_ASSERT(a_idx >= 0 && a_idx < m_vec.size());
314  return m_vec[a_idx];
315  }
316 
319  void push_back(const VecIntPy& a_value) { m_vec.push_back(a_value); }
320 
323  int size() const { return (int)m_vec.size(); }
324 
327  bool empty() const { return m_vec.empty(); }
328 
334  void resize(int a_size, const VecIntPy& a_vec = VecIntPy()) { m_vec.resize(a_size, a_vec); }
335 
336 private:
337  std::vector<VecIntPy> m_vec;
338 };
339 
340 class MeWeightMatcherImpl : public MeWeightMatcher
341 {
342 public:
343  MeWeightMatcherImpl();
344 
345  void Init(const VecMeEdge& a_edges);
346  virtual VecInt MatchWeights(const VecMeEdge& a_edges, bool a_maxCardinality = false) override;
347 
348  int slack(int a_edge);
349  VecIntPy blossomLeaves(int b);
350  void assignLabel(int w, int t, int p);
351  int scanBlossom(int v, int w);
352  void addBlossom(int base, int k);
353  void expandBlossom(int b, bool endstage);
354  void augmentBlossom(int b, int v);
355  void augmentMatching(int k);
356  void verifyOptimum(bool maxcardinality);
357  void checkDelta2();
358  void checkDelta3();
359 
360 private:
361  const MeEdge& GetEdge(int a_idx);
362 
364 
365  int m_nEdge;
366  int m_nVertex;
368 
374  VecIntPy m_endPoint;
375 
379  VecInt2dPy m_neighbEnd;
380 
385  VecIntPy m_mate;
386 
396  VecIntPy m_label;
397 
404  VecIntPy m_labelEnd;
405 
411  VecIntPy m_inBlossom;
412 
416  VecIntPy m_blossomParent;
417 
422  VecInt2dPy m_blossomChildren;
423 
427  VecIntPy m_blossomBase;
428 
434  VecInt2dPy m_blossomEndPts;
435 
443  VecIntPy m_bestEdge;
444 
449  VecInt2dPy m_blossomBestEdges;
450 
454 
463  VecIntPy m_dualVar;
464 
469  VecIntPy m_queue;
470 };
471 
477 //------------------------------------------------------------------------------
479 //------------------------------------------------------------------------------
480 MeWeightMatcherImpl::MeWeightMatcherImpl()
481 {
482 } // MeWeightMatcherImpl::MeWeightMatcherImpl
483 //------------------------------------------------------------------------------
486 //------------------------------------------------------------------------------
487 void MeWeightMatcherImpl::Init(const VecMeEdge& a_edges)
488 {
489  m_edges = a_edges;
490  m_nEdge = (int)a_edges.size();
491  m_nVertex = 0;
492  m_maxWeight = 0;
493 
494  // Count vertices.
495  // Find the maximum edge weight.
496 
497  // Edges are numbered 0 .. (m_nEdge-1).
498  // Edge endpoints are numbered 0 .. (2*m_nEdge-1), such that endpoints
499  // (2*k) and (2*k+1) both belong to edge k.
500  // If p is an edge endpoint,
501  // m_endPoint[p] is the vertex to which endpoint p is attached.
502  // Not modified by the algorithm.
503  m_endPoint.reserve(m_nEdge * 2);
504 
505  for (auto& edge : m_edges)
506  {
507  const int& i = edge.m_f0;
508  const int& j = edge.m_f1;
509  XM_ASSERT(i >= 0 && j >= 0 && i != j);
510  if (i >= m_nVertex)
511  m_nVertex = i + 1;
512  if (j >= m_nVertex)
513  m_nVertex = j + 1;
514  m_maxWeight = std::max(m_maxWeight, edge.m_weight);
515 
516  m_endPoint.push_back(i);
517  m_endPoint.push_back(j);
518  }
519 
520  // If v is a vertex,
521  // m_neighbEnd[v] is the list of remote endpoints of the edges attached to v.
522  // Not modified by the algorithm.
523  m_neighbEnd.resize(m_nVertex, VecIntPy());
524  for (int k = 0; k < (int)m_edges.size(); ++k)
525  {
526  const MeEdge& edge = m_edges[k];
527  const int& i = edge.m_f0;
528  const int& j = edge.m_f1;
529  m_neighbEnd[i].push_back(2 * k + 1);
530  m_neighbEnd[j].push_back(2 * k);
531  }
532 
533  // If v is a vertex,
534  // m_mate[v] is the remote endpoint of its matched edge, or -1 if it is single
535  // (i.e. endpoint[m_mate[v]] is v's partner vertex).
536  // Initially all vertices are single; updated during augmentation.
537  m_mate.resize(m_nVertex, -1);
538 
539  // If b is a top-level blossom,
540  // m_label[b] is 0 if b is unlabeled (free);
541  // 1 if b is an S-vertex/blossom;
542  // 2 if b is a T-vertex/blossom.
543  // The label of a vertex is found by looking at the label of its
544  // top-level containing blossom.
545  // If v is a vertex inside a T-blossom,
546  // m_label[v] is 2 iff v is reachable from an S-vertex outside the blossom.
547  // Labels are assigned during a stage and reset after each augmentation.
548  m_label.resize(2 * m_nVertex, 0);
549 
550  // If b is a labeled top-level blossom,
551  // m_labelEnd[b] is the remote endpoint of the edge through which b obtained
552  // its label, or -1 if b's base vertex is single.
553  // If v is a vertex inside a T-blossom and m_label[v] == 2,
554  // m_labelEnd[v] is the remote endpoint of the edge through which v is
555  // reachable from outside the blossom.
556  m_labelEnd.resize(2 * m_nVertex, -1);
557 
558  // If v is a vertex,
559  // m_inBlossom[v] is the top-level blossom to which v belongs.
560  // If v is a top-level vertex, v is itself a blossom (a trivial blossom)
561  // and m_inBlossom[v] == v.
562  // Initially all vertices are top-level trivial blossoms.
563  // if m_nVertex is 4: [0, 1, 2, 3]
564  m_inBlossom.resize_to_count(m_nVertex);
565 
566  // If b is a sub-blossom,
567  // m_blossomParent[b] is its immediate parent (sub-)blossom.
568  // If b is a top-level blossom, m_blossomParent[b] is -1.
569  m_blossomParent.resize(2 * m_nVertex, -1);
570 
571  // If b is a non-trivial (sub-)blossom,
572  // m_blossomChildren[b] is an ordered list of its sub-blossoms, starting with
573  // the base and going round the blossom.
574  // m_blossomChildren = (2 * m_nVertex) * [ None ]
575  m_blossomChildren.resize(2 * m_nVertex, VecInt());
576 
577  // If b is a (sub-)blossom,
578  // m_blossomBase[b] is its base VERTEX (i.e. recursive sub-blossom).
579  // if m_nVertex is 4: [0, 1, 2, 3, -1, -1, -1, -1]
580  m_blossomBase.resize_to_count(m_nVertex);
581  m_blossomBase.resize(2 * m_nVertex, -1);
582 
583  // If b is a non-trivial (sub-)blossom,
584  // m_blossomEndPts[b] is a list of endpoints on its connecting edges,
585  // such that m_blossomEndPts[b][i] is the local endpoint of
586  // m_blossomChildren[b][i] on the edge that connects it to
587  // m_blossomChildren[b][wrap(i+1)].
588  m_blossomEndPts.resize(2 * m_nVertex, VecIntPy());
589 
590  // If v is a free vertex (or an unreached vertex inside a T-blossom),
591  // m_bestEdge[v] is the edge to an S-vertex with least slack,
592  // or -1 if there is no such edge.
593  // If b is a (possibly trivial) top-level S-blossom,
594  // m_bestEdge[b] is the least-slack edge to a different S-blossom,
595  // or -1 if there is no such edge.
596  // This is used for efficient computation of delta2 and delta3.
597  m_bestEdge.resize(2 * m_nVertex, -1);
598 
599  // If b is a non-trivial top-level S-blossom,
600  // m_blossomBestEdges[b] is a list of least-slack edges to neighbouring
601  // S-blossoms, or None if no such list has been computed yet.
602  // This is used for efficient computation of delta3.
603  // m_blossomBestEdges = (2 * m_nVertex) * [ None ]
604  m_blossomBestEdges.resize(2 * m_nVertex, VecIntPy());
605 
606  // List of currently unused blossom numbers.
607  // if m_nVertex is 4: [4, 5, 6, 7]
608  m_unusedBlossoms.resize_to_count(m_nVertex, m_nVertex);
609 
610  // If v is a vertex,
611  // m_dualVar[v] = 2 * u(v) where u(v) is the v's variable in the dual
612  // optimization problem (multiplication by two ensures integer values
613  // throughout the algorithm if all edge weights are integers).
614  // If b is a non-trivial blossom,
615  // m_dualVar[b] = z(b) where z(b) is b's variable in the dual optimization
616  // problem.
617  // if m_nVertex is 4 and m_maxWeight is w: [w, w, w, w, 0, 0, 0, 0]
619  m_dualVar.resize(m_nVertex * 2, 0);
620 
621  // If m_allowEdge[k] is true, edge k has zero slack in the optimization
622  // problem; if m_allowEdge[k] is false, the edge's slack may or may not
623  // be zero.
624  m_allowEdge.resize(m_nEdge, false);
625 } // MeWeightMatcherImpl::Init
626 //------------------------------------------------------------------------------
635 //------------------------------------------------------------------------------
636 VecInt MeWeightMatcherImpl::MatchWeights(const VecMeEdge& a_edges,
637  bool a_maxCardinality /* = false*/)
638 {
639  Init(a_edges);
640 
641  // Deal swiftly with empty graphs.
642  if (m_edges.empty())
643  {
644  return VecInt();
645  }
646 
647  m_mate.clear();
648  m_mate.resize(m_nVertex, -1);
649  int BOGUS_SLACK = 0xdeadbeef;
650 
651  // Main loop: continue until no further improvement is possible.
652  for (int t = 0; t < m_nVertex; ++t)
653  {
654  // Each iteration of this loop is a "stage".
655  // A stage finds an augmenting path and uses that to improve
656  // the matching.
657 #ifdef DEBUG
658  printf("STAGE %d\n", t);
659 #endif
660 
661  // Remove labels from top-level blossoms/vertices.
662  m_label.clear();
663  m_label.resize(2 * m_nVertex, 0);
664 
665  // Forget all about least-slack edges.
666  m_bestEdge.clear();
667  m_bestEdge.resize(2 * m_nVertex, -1);
668 
669  for (int i = m_nVertex; i < m_nVertex * 2; ++i)
670  {
671  m_blossomBestEdges[i].clear();
672  }
673 
674  // Loss of labeling means that we can not be sure that currently
675  // allowable edges remain allowable througout this stage.
676  m_allowEdge.clear();
677  m_allowEdge.resize(m_nEdge, false);
678 
679  // Make m_queue empty.
680  m_queue.clear();
681 
682  // Label single blossoms/vertices with S and put them in the m_queue.
683  for (int v = 0; v < m_nVertex; ++v)
684  {
685  if (m_mate[v] == -1 && m_label[m_inBlossom[v]] == 0)
686  {
687  assignLabel(v, 1, -1);
688  }
689  }
690  // Loop until we succeed in augmenting the matching.
691  bool augmented = false;
692  while (true)
693  {
694  // Each iteration of this loop is a "substage".
695  // A substage tries to find an augmenting path;
696  // if found, the path is used to improve the matching and
697  // the stage ends. If there is no augmenting path, the
698  // primal-dual method is used to pump some slack out of
699  // the dual variables.
700 #ifdef DEBUG
701  printf("SUBSTAGE\n");
702 #endif
703  // Continue labeling until all vertices which are reachable
704  // through an alternating path have got a label.
705  while (!m_queue.empty() && !augmented)
706  {
707  // Take an S vertex from the m_queue.
708  int v = m_queue.pop();
709 #ifdef DEBUG
710  printf("POP v=%d\n", v);
711 #endif
712  XM_ASSERT(m_label[m_inBlossom[v]] == 1);
713 
714  // Scan its neighbours:
715  for (int idx = 0; idx < m_neighbEnd[v].size(); ++idx)
716  {
717  int& p = m_neighbEnd[v][idx];
718  int k = p / 2;
719  int w = m_endPoint[p];
720  // w is a neighbour to v
721  if (m_inBlossom[v] == m_inBlossom[w])
722  {
723  // this edge is internal to a blossom; ignore it
724  continue;
725  }
726  int kslack = BOGUS_SLACK;
727  bool valid_kslack = false;
728  if (!m_allowEdge[k])
729  {
730  kslack = slack(k);
731  valid_kslack = true;
732  if (kslack <= 0)
733  {
734  // edge k has zero slack => it is allowable
735  m_allowEdge[k] = true;
736  }
737  }
738 
739  if (m_allowEdge[k])
740  {
741  if (m_label[m_inBlossom[w]] == 0)
742  {
743  // (C1) w is a free vertex;
744  // label w with T and label its mate with S (R12).
745  assignLabel(w, 2, p ^ 1);
746  }
747  else if (m_label[m_inBlossom[w]] == 1)
748  {
749  // (C2) w is an S-vertex (not in the same blossom);
750  // follow back-links to discover either an
751  // augmenting path or a new blossom.
752  int base = scanBlossom(v, w);
753  if (base >= 0)
754  {
755  // Found a new blossom; add it to the blossom
756  // bookkeeping and turn it into an S-blossom.
757  addBlossom(base, k);
758  }
759  else
760  {
761  // Found an augmenting path; augment the
762  // matching and end this stage.
763  augmentMatching(k);
764  augmented = true;
765  break;
766  }
767  }
768  else if (m_label[w] == 0)
769  {
770  // w is inside a T-blossom, but w itself has not
771  // yet been reached from outside the blossom;
772  // mark it as reached (we need this to relabel
773  // during T-blossom expansion).
774  XM_ASSERT(m_label[m_inBlossom[w]] == 2);
775  m_label[w] = 2;
776  m_labelEnd[w] = p ^ 1;
777  }
778  }
779  else if (m_label[m_inBlossom[w]] == 1)
780  {
781  // keep track of the least-slack non-allowable edge to
782  // a different S-blossom.
783  int b = m_inBlossom[v];
784  if (m_bestEdge[b] == -1 || (valid_kslack && kslack < slack(m_bestEdge[b])))
785  {
786  m_bestEdge[b] = k;
787  }
788  }
789  else if (m_label[w] == 0)
790  {
791  // w is a free vertex (or an unreached vertex inside
792  // a T-blossom) but we can not reach it yet;
793  // keep track of the least-slack edge that reaches w.
794  if (m_bestEdge[w] == -1 || (valid_kslack && kslack < slack(m_bestEdge[w])))
795  {
796  m_bestEdge[w] = k;
797  }
798  }
799  }
800  }
801  if (augmented)
802  {
803  break;
804  }
805 
806  // There is no augmenting path under these constraints;
807  // compute delta and reduce slack in the optimization problem.
808  // (Note that our vertex dual variables, edge slacks and delta's
809  // are pre-multiplied by two.)
810  int deltatype = -1;
811  int NONE = -1;
812  int deltaedge = NONE;
813  int delta = NONE;
814  int deltablossom = NONE;
815 
816  // Verify data structures for delta2/delta3 computation.
817 #ifdef CHECK_DELTA
818  checkDelta2();
819  checkDelta3();
820 #endif
821 
822  // Compute delta1: the minumum value of any vertex dual.
823  if (!a_maxCardinality)
824  {
825  deltatype = 1;
826  delta = m_dualVar.min(0, m_nVertex);
827  }
828 
829  // Compute delta2: the minimum slack on any edge between
830  // an S-vertex and a free vertex.
831  int dslack = BOGUS_SLACK;
832  for (int v = 0; v < m_nVertex; ++v)
833  {
834  if (m_label[m_inBlossom[v]] == 0 && m_bestEdge[v] != -1)
835  {
836  dslack = slack(m_bestEdge[v]);
837  if (deltatype == -1 || dslack < delta)
838  {
839  delta = dslack;
840  deltatype = 2;
841  deltaedge = m_bestEdge[v];
842  }
843  }
844  }
845 
846  // Compute delta3: half the minimum slack on any edge between
847  // a pair of S-blossoms.
848  for (int b = 0; b < 2 * m_nVertex; ++b)
849  {
850  if (m_blossomParent[b] == -1 && m_label[b] == 1 && m_bestEdge[b] != -1)
851  {
852  int kslack = slack(m_bestEdge[b]);
853  XM_ASSERT((kslack % 2) == 0);
854  dslack = kslack / 2;
855  if (deltatype == -1 || (dslack < delta))
856  {
857  delta = dslack;
858  deltatype = 3;
859  deltaedge = m_bestEdge[b];
860  }
861  }
862  }
863 
864  // Compute delta4: minimum z variable of any T-blossom.
865  for (int b = m_nVertex; b < 2 * m_nVertex; ++b)
866  {
867  if (m_blossomBase[b] >= 0 && m_blossomParent[b] == -1 && m_label[b] == 2 &&
868  (deltatype == -1 || m_dualVar[b] < delta))
869  {
870  delta = m_dualVar[b];
871  deltatype = 4;
872  deltablossom = b;
873  }
874  }
875 
876  if (deltatype == -1)
877  {
878  // No further improvement possible; max-cardinality optimum
879  // reached. Do a final delta update to make the optimum
880  // verifyable.
881  XM_ASSERT(a_maxCardinality);
882  deltatype = 1;
883  delta = std::max(0, m_dualVar.min(0, m_nVertex));
884  }
885 
886  // Update dual variables according to delta.
887  for (int v = 0; v < m_nVertex; ++v)
888  {
889  if (m_label[m_inBlossom[v]] == 1)
890  {
891  // S-vertex: 2*u = 2*u - 2*delta
892  m_dualVar[v] -= delta;
893  }
894  else if (m_label[m_inBlossom[v]] == 2)
895  {
896  // T-vertex: 2*u = 2*u + 2*delta
897  m_dualVar[v] += delta;
898  }
899  }
900  for (int b = m_nVertex; b < 2 * m_nVertex; ++b)
901  {
902  if (m_blossomBase[b] >= 0 && m_blossomParent[b] == -1)
903  {
904  if (m_label[b] == 1)
905  {
906  // top-level S-blossom: z = z + 2*delta
907  m_dualVar[b] += delta;
908  }
909  else if (m_label[b] == 2)
910  {
911  // top-level T-blossom: z = z - 2*delta
912  m_dualVar[b] -= delta;
913  }
914  }
915  }
916 
917  // Take action at the point where minimum delta occurred.
918 #ifdef DEBUG
919  printf("delta%d=%d\n", deltatype, delta);
920 #endif
921  if (deltatype == 1)
922  {
923  // No further improvement possible; optimum reached.
924  break;
925  }
926  else if (deltatype == 2)
927  {
928  // Use the least-slack edge to continue the search.
929  m_allowEdge[deltaedge] = true;
930  auto& edge = GetEdge(deltaedge);
931  int i = edge.m_f0;
932  int j = edge.m_f1;
933  if (m_label[m_inBlossom[i]] == 0)
934  {
935  std::swap(i, j);
936  }
937  XM_ASSERT(m_label[m_inBlossom[i]] == 1);
938  m_queue.push_back(i);
939  }
940  else if (deltatype == 3)
941  {
942  // Use the least-slack edge to continue the search.
943  m_allowEdge[deltaedge] = true;
944  auto& edge = GetEdge(deltaedge);
945  int i = edge.m_f0;
946  XM_ASSERT(m_label[m_inBlossom[i]] == 1);
947  m_queue.push_back(i);
948  }
949  else if (deltatype == 4)
950  {
951  // Expand the least-z blossom.
952  expandBlossom(deltablossom, false);
953  }
954  // End of a this substage.
955  }
956 
957  // Stop when no more augmenting path can be found.
958  if (!augmented)
959  {
960  break;
961  }
962 
963  // End of a stage; expand all S-blossoms which have m_dualVar = 0.
964  for (int b = m_nVertex; b < 2 * m_nVertex; ++b)
965  {
966  if (m_blossomParent[b] == -1 && m_blossomBase[b] >= 0 && m_label[b] == 1 && m_dualVar[b] == 0)
967  {
968  expandBlossom(b, true);
969  }
970  }
971  }
972 
973  // Verify that we reached the optimum solution.
974 #ifdef CHECK_OPTIMUM
975  verifyOptimum(a_maxCardinality);
976 #endif
977 
978  // Transform m_mate[] such that m_mate[v] is the vertex to which v is paired.
979  for (int v = 0; v < m_nVertex; ++v)
980  {
981  if (m_mate[v] >= 0)
982  {
983  m_mate[v] = m_endPoint[m_mate[v]];
984  }
985  }
986  for (int v = 0; v < m_nVertex; ++v)
987  {
988  XM_ASSERT(m_mate[v] == -1 || m_mate[m_mate[v]] == v);
989  }
990  return m_mate.ToVecInt();
991 } // MeWeightMatcherImpl::MatchWeights
992 //------------------------------------------------------------------------------
996 //------------------------------------------------------------------------------
997 int MeWeightMatcherImpl::slack(int a_k)
998 {
999  if (a_k < 0)
1000  {
1001  a_k = (int)m_edges.size() + a_k;
1002  }
1003  XM_ASSERT(a_k < m_edges.size());
1004  const MeEdge& edge = GetEdge(a_k);
1005  const int& i = edge.m_f0;
1006  const int& j = edge.m_f1;
1007  const int& wt = edge.m_weight;
1008  return m_dualVar[i] + m_dualVar[j] - 2 * wt;
1009 } // MeWeightMatcherImpl::slack
1010 //------------------------------------------------------------------------------
1014 //------------------------------------------------------------------------------
1015 VecIntPy MeWeightMatcherImpl::blossomLeaves(int a_b)
1016 {
1017  VecIntPy results;
1018  if (a_b < m_nVertex)
1019  {
1020  results.push_back(a_b);
1021  return results;
1022  }
1023 
1024  results = m_blossomChildren[a_b];
1025 #ifdef DEBUG
1026  printf("blossomLeaves(%d) - blossomchilds[b] = %s\n", a_b, TS_AS_STRING(results.ToVecInt()));
1027 #endif
1028 
1029  while (results.max() > m_nVertex)
1030  {
1031  // replace big values
1032  VecIntPy newresults;
1033  for (int idx = 0; idx < results.size(); ++idx)
1034  {
1035  int elem = results[idx];
1036  if (elem < m_nVertex)
1037  {
1038  newresults.push_back(elem);
1039  }
1040  else
1041  {
1042  newresults.append(m_blossomChildren[elem]);
1043  }
1044  }
1045  results = newresults;
1046  }
1047  return results;
1048 } // MeWeightMatcherImpl::blossomLeaves
1049 //------------------------------------------------------------------------------
1055 //------------------------------------------------------------------------------
1056 void MeWeightMatcherImpl::assignLabel(int a_w, int a_t, int a_p)
1057 {
1058 #ifdef DEBUG
1059  printf("assignLabel(%d,%d,%d)\n", a_w, a_t, a_p);
1060 #endif
1061  int b = m_inBlossom[a_w];
1062  XM_ASSERT(m_label[a_w] == 0 && m_label[b] == 0);
1063  m_label[a_w] = m_label[b] = a_t;
1064  m_labelEnd[a_w] = m_labelEnd[b] = a_p;
1065  m_bestEdge[a_w] = m_bestEdge[b] = -1;
1066  if (a_t == 1)
1067  {
1068  // b became an S-vertex/blossom; add it(s vertices) to the m_queue.
1069  VecIntPy bleaves = blossomLeaves(b);
1070  m_queue.append(bleaves);
1071 #ifdef DEBUG
1072  printf("PUSH %s\n", TS_AS_STRING(bleaves.ToVecInt()));
1073 #endif
1074  }
1075  else if (a_t == 2)
1076  {
1077  // b became a T-vertex/blossom; assign label S to its mate.
1078  // (If b is a non-trivial blossom, its base is the only vertex
1079  // with an external mate.)
1080  int base = m_blossomBase[b];
1081  int mateBase = m_mate[base];
1082  XM_ASSERT(mateBase >= 0);
1083  assignLabel(m_endPoint[mateBase], 1, mateBase ^ 1);
1084  }
1085 } // MeWeightMatcherImpl::assignLabel
1086 //------------------------------------------------------------------------------
1092 //------------------------------------------------------------------------------
1093 int MeWeightMatcherImpl::scanBlossom(int a_v, int a_w)
1094 {
1095 #ifdef DEBUG
1096  printf("scanBlossom(%d, %d)\n", a_v, a_w);
1097 #endif
1098  // Trace back from v and w, placing beadcrumbs as we go.
1099  VecIntPy path;
1100  int base = -1;
1101  while (a_v != -1 || a_w != -1)
1102  {
1103  // Look for a breadcrumb in v's blossom or put a new breadcrumb.
1104  int b = m_inBlossom[a_v];
1105  if (m_label[b] & 4)
1106  {
1107  base = m_blossomBase[b];
1108  break;
1109  }
1110  XM_ASSERT(m_label[b] == 1);
1111  path.push_back(b);
1112  m_label[b] = 5;
1113  // Trace one step back.
1115  if (m_labelEnd[b] == -1)
1116  {
1117  // The base of blossom b is single; stop stracing this path.
1118  a_v = -1;
1119  }
1120  else
1121  {
1122  a_v = m_endPoint[m_labelEnd[b]];
1123  b = m_inBlossom[a_v];
1124  XM_ASSERT(m_label[b] == 2);
1125  // b is a T-blossom; trace one more step back.
1126  XM_ASSERT(m_labelEnd[b] >= 0);
1127  a_v = m_endPoint[m_labelEnd[b]];
1128  }
1129  // Swap v and w so that we alternate between both paths.
1130  if (a_w != -1)
1131  {
1132  std::swap(a_v, a_w);
1133  }
1134  }
1135  // Remove breadcrumbs.
1136  for (int idx = 0; idx < path.size(); ++idx)
1137  {
1138  int b = path[idx];
1139  m_label[b] = 1;
1140  }
1141  // Return base vertex, if we found one.
1142  return base;
1143 } // MeWeightMatcherImpl::scanBlossom
1144 //------------------------------------------------------------------------------
1151 //------------------------------------------------------------------------------
1152 void MeWeightMatcherImpl::addBlossom(int a_base, int a_k)
1153 {
1154  const MeEdge& edge = GetEdge(a_k);
1155  int v = edge.m_f0;
1156  int w = edge.m_f1;
1157  int bb = m_inBlossom[a_base];
1158  int bv = m_inBlossom[v];
1159  int bw = m_inBlossom[w];
1160  // Create blossom.
1161  int b = m_unusedBlossoms.pop();
1162 #ifdef DEBUG
1163  printf("addBlossom(%d,%d) (v=%d w=%d) -> %d\n", a_base, a_k, v, w, b);
1164 #endif
1165  m_blossomBase[b] = a_base;
1166  m_blossomParent[b] = -1;
1167  m_blossomParent[bb] = b;
1168  // Make list of sub-blossoms and their interconnecting edge endpoints.
1169  m_blossomChildren[b].clear();
1170  VecIntPy& path = m_blossomChildren[b];
1171 #ifdef DEBUG
1172  printf("blossomchilds[%d] cleared\n", b);
1173 #endif
1174  m_blossomEndPts[b].clear();
1175  VecIntPy& endps = m_blossomEndPts[b];
1176  // Trace back from v to base.
1177  while (bv != bb)
1178  {
1179  // Add bv to the new blossom.
1180  m_blossomParent[bv] = b;
1181  path.push_back(bv);
1182  endps.push_back(m_labelEnd[bv]);
1183  XM_ASSERT(m_label[bv] == 2 ||
1184  (m_label[bv] == 1 && m_labelEnd[bv] == m_mate[m_blossomBase[bv]]));
1185  // Trace one step back.
1186  XM_ASSERT(m_labelEnd[bv] >= 0);
1187  v = m_endPoint[m_labelEnd[bv]];
1188  bv = m_inBlossom[v];
1189  }
1190  // Reverse lists, add endpoint that connects the pair of S vertices.
1191  path.push_back(bb);
1192  path.reverse();
1193  endps.reverse();
1194  endps.push_back(2 * a_k);
1195  // Trace back from w to base.
1196  while (bw != bb)
1197  {
1198  // Add bw to the new blossom.
1199  m_blossomParent[bw] = b;
1200  path.push_back(bw);
1201  endps.push_back(m_labelEnd[bw] ^ 1);
1202  XM_ASSERT(m_label[bw] == 2 ||
1203  (m_label[bw] == 1 && m_labelEnd[bw] == m_mate[m_blossomBase[bw]]));
1204  // Trace one step back;
1205  XM_ASSERT(m_labelEnd[bw] >= 0);
1206  w = m_endPoint[m_labelEnd[bw]];
1207  bw = m_inBlossom[w];
1208  }
1209  // Set label to S.
1210  XM_ASSERT(m_label[bb] == 1);
1211  m_label[b] = 1;
1212  m_labelEnd[b] = m_labelEnd[bb];
1213  // Set dual variable to zero.
1214  m_dualVar[b] = 0;
1215  // Relabel vertices.
1216 
1217  VecIntPy bleaves = blossomLeaves(b);
1218  for (int idx = 0; idx < bleaves.size(); ++idx)
1219  {
1220  v = bleaves[idx];
1221  if (m_label[m_inBlossom[v]] == 2) // is T-vertex
1222  {
1223  // This T-vertex now turns into an S-vertex because it becomes
1224  // part of an S-blossom; add it to the m_queue.
1225  m_queue.push_back(v);
1226  }
1227  m_inBlossom[v] = b;
1228  }
1229 
1230  // Compute m_blossomBestEdges[b].
1231  VecIntPy bestedgeto;
1232  bestedgeto.resize(2 * m_nVertex, -1);
1233  for (int idx = 0; idx < path.size(); ++idx)
1234  {
1235  int bv = path[idx];
1236  VecInt2dPy nblists;
1237  if (m_blossomBestEdges[bv].empty())
1238  {
1239  // This subblossom does not have a list of least-slack edges;
1240  // get the information from the vertices.
1241  VecIntPy nblist;
1242  for (int idx2 = 0; idx2 < m_neighbEnd[v].size(); ++idx2)
1243  {
1244  int p = m_neighbEnd[v][idx2];
1245  nblist.push_back(p / 2);
1246  }
1247  nblists.resize(blossomLeaves(bv).size(), nblist);
1248  }
1249  else
1250  {
1251  // Walk this subblossom's least-slack edges.
1252  nblists.resize(1);
1253  nblists[0] = m_blossomBestEdges[bv];
1254  }
1255  for (int idx3 = 0; idx3 < nblists.size(); ++idx3)
1256  {
1257  auto& nblist = nblists[idx3];
1258  for (int idx4 = 0; idx4 < nblist.size(); ++idx4)
1259  {
1260  int k = nblist[idx4];
1261  const MeEdge& edge = GetEdge(k);
1262  int i = edge.m_f0;
1263  int j = edge.m_f1;
1264  if (m_inBlossom[j] == b)
1265  {
1266  std::swap(i, j);
1267  }
1268  int bj = m_inBlossom[j];
1269  if (bj != b && m_label[bj] == 1 &&
1270  (bestedgeto[bj] == -1 || slack(k) < slack(bestedgeto[bj])))
1271  {
1272  bestedgeto[bj] = k;
1273  }
1274  }
1275  }
1276  // Forget about least-slack edges of the subblossom.
1277  m_blossomBestEdges[bv].clear();
1278  m_bestEdge[bv] = -1;
1279  }
1280  VecIntPy bk;
1281  for (int idx = 0; idx < bestedgeto.size(); ++idx)
1282  {
1283  int k = bestedgeto[idx];
1284  if (k != -1)
1285  {
1286  bk.push_back(k);
1287  }
1288  }
1289  m_blossomBestEdges[b] = bk; // [ k for k in bestedgeto if k != -1 ]
1290  // Select m_bestEdge[b]
1291  m_bestEdge[b] = -1;
1292  for (int idx = 0; idx < bk.size(); ++idx)
1293  {
1294  int k = bk[idx];
1295  if (m_bestEdge[b] == -1 || slack(k) < slack(m_bestEdge[b]))
1296  {
1297  m_bestEdge[b] = k;
1298  }
1299  }
1300 #ifdef DEBUG
1301  printf("blossomchilds[%d]=%s\n", b, TS_AS_STRING(m_blossomChildren[b].ToVecInt()));
1302 #endif
1303 } // MeWeightMatcherImpl::addBlossom
1304 //------------------------------------------------------------------------------
1308 //------------------------------------------------------------------------------
1309 void MeWeightMatcherImpl::expandBlossom(int a_b, bool a_endStage)
1310 {
1311 #ifdef DEBUG
1312  printf("expandBlossom(%d,%d) %s\n", a_b, a_endStage,
1313  TS_AS_STRING(m_blossomChildren[a_b].ToVecInt()));
1314 #endif
1315  // Convert sub-blossoms into top-level blossoms.
1316  for (int idx = 0; idx < m_blossomChildren[a_b].size(); ++idx)
1317  {
1318  int s = m_blossomChildren[a_b][idx];
1319  m_blossomParent[s] = -1;
1320  if (s < m_nVertex)
1321  {
1322  m_inBlossom[s] = s;
1323  }
1324  else if (a_endStage && m_dualVar[s] == 0)
1325  {
1326  // Recursively expand this sub-blossom.
1327  expandBlossom(s, a_endStage);
1328  }
1329  else
1330  {
1331  VecIntPy bleaves = blossomLeaves(s);
1332  for (int idx2 = 0; idx2 < bleaves.size(); ++idx2)
1333  {
1334  int v = bleaves[idx2];
1335  m_inBlossom[v] = s;
1336  }
1337  }
1338  }
1339 
1340  // If we expand a T-blossom during a stage, its sub-blossoms must be
1341  // relabeled.
1342  if (!a_endStage && m_label[a_b] == 2)
1343  {
1344  // Start at the sub-blossom through which the expanding
1345  // blossom obtained its label, and relabel sub-blossoms untili
1346  // we reach the base.
1347  // Figure out through which sub-blossom the expanding blossom
1348  // obtained its label initially.
1349  XM_ASSERT(m_labelEnd[a_b] >= 0);
1350  int entrychild = m_inBlossom[m_endPoint[m_labelEnd[a_b] ^ 1]];
1351  // Decide in which direction we will go round the blossom.
1352  int j = m_blossomChildren[a_b].index(entrychild);
1353  // Assume start index is even; go backward.
1354  int jstep = -1;
1355  int endptrick = 1;
1356  if (j & 1)
1357  {
1358  // Start index is odd; go forward and wrap.
1359  j -= (int)m_blossomChildren[a_b].size();
1360  jstep = 1;
1361  endptrick = 0;
1362  }
1363 
1364  // Move along the blossom until we get to the base.
1365  int p = m_labelEnd[a_b];
1366  while (j != 0)
1367  {
1368  // Relabel the T-sub-blossom.
1369  m_label[m_endPoint[p ^ 1]] = 0;
1370  m_label[m_endPoint[m_blossomEndPts[a_b][j - endptrick] ^ endptrick ^ 1]] = 0;
1371  assignLabel(m_endPoint[p ^ 1], 2, p);
1372  // Step to the next S-sub-blossom and note its forward endpoint.
1373  m_allowEdge[m_blossomEndPts[a_b][j - endptrick] / 2] = true;
1374  j += jstep;
1375  p = m_blossomEndPts[a_b][j - endptrick] ^ endptrick;
1376  // Step to the next T-sub-blossom.
1377  m_allowEdge[p / 2] = true;
1378  j += jstep;
1379  }
1380  // Relabel the base T-sub-blossom WITHOUT stepping through to
1381  // its mate (so don't call assignLabel).
1382  int bv = m_blossomChildren[a_b][j];
1383  m_label[m_endPoint[p ^ 1]] = m_label[bv] = 2;
1384  m_labelEnd[m_endPoint[p ^ 1]] = m_labelEnd[bv] = p;
1385  m_bestEdge[bv] = -1;
1386  // Continue along the blossom until we get back to entrychild.
1387  j += jstep;
1388  while (m_blossomChildren[a_b][j] != entrychild)
1389  {
1390  // Examine the vertices of the sub-blossom to see whether
1391  // it is reachable from a neighbouring S-vertex outside the
1392  // expanding blossom.
1393  bv = m_blossomChildren[a_b][j];
1394  if (m_label[bv] == 1)
1395  {
1396  // This sub-blossom just got label S through one of its
1397  // neighbours; leave it.
1398  j += jstep;
1399  continue;
1400  }
1401  VecIntPy bleaves = blossomLeaves(bv);
1402  XM_ASSERT(!bleaves.empty());
1403  int v; // get where zero or last item in bleaves
1404  for (int idx2 = 0; idx2 < bleaves.size(); ++idx2)
1405  {
1406  v = bleaves[idx2];
1407  if (m_label[v] != 0)
1408  {
1409  break;
1410  }
1411  }
1412  // If the sub-blossom contains a reachable vertex, assign
1413  // label T to the sub-blossom.
1414  if (m_label[v] != 0)
1415  {
1416  XM_ASSERT(m_label[v] == 2);
1417  XM_ASSERT(m_inBlossom[v] == bv);
1418  m_label[v] = 0;
1420  assignLabel(v, 2, m_labelEnd[v]);
1421  }
1422  j += jstep;
1423  }
1424  }
1425  // Recycle the blossom number.
1426  m_label[a_b] = m_labelEnd[a_b] = -1;
1427  m_blossomChildren[a_b].clear();
1428 #ifdef DEBUG
1429  printf("clear blossomchilds[%d]\n", a_b);
1430 #endif
1431  m_blossomEndPts[a_b].clear();
1432  m_blossomBase[a_b] = -1;
1433  m_blossomBestEdges[a_b].clear();
1434  m_bestEdge[a_b] = -1;
1435  m_unusedBlossoms.push_back(a_b);
1436 } // MeWeightMatcherImpl::expandBlossom
1437 //------------------------------------------------------------------------------
1443 //------------------------------------------------------------------------------
1444 void MeWeightMatcherImpl::augmentBlossom(int a_b, int a_v)
1445 {
1446 #ifdef DEBUG
1447  printf("augmentBlossom(%d,%d)\n", a_b, a_v);
1448 #endif
1449  // Bubble up through the blossom tree from vertex v to an immediate
1450  // sub-blossom of b.
1451  int t = a_v;
1452  while (m_blossomParent[t] != a_b)
1453  {
1454  t = m_blossomParent[t];
1455  }
1456  // Recursively deal with the first sub-blossom.
1457  if (t >= m_nVertex)
1458  {
1459  augmentBlossom(t, a_v);
1460  }
1461  // Decide in which direction we will go round the blossom.
1462  int i = m_blossomChildren[a_b].index(t);
1463  int j = i;
1464  // Assume start index is even; go backward.
1465  int jstep = -1;
1466  int endptrick = 1;
1467  if (i & 1)
1468  {
1469  // Start index is odd; go forward and wrap.
1470  j -= (int)m_blossomChildren[a_b].size();
1471  jstep = 1;
1472  endptrick = 0;
1473  }
1474  // Move along the blossom until we get to the base.
1475  while (j != 0)
1476  {
1477  // Step to the next sub-blossom and augment it recursively.
1478  j += jstep;
1479  t = m_blossomChildren[a_b][j];
1480  int p = m_blossomEndPts[a_b][j - endptrick] ^ endptrick;
1481  if (t >= m_nVertex)
1482  {
1483 #ifdef DEBUG
1484  printf("augmentBlossom 1\n");
1485 #endif
1486  augmentBlossom(t, m_endPoint[p]);
1487  }
1488  // Step to the next sub-blossom and augment it recursively.
1489  j += jstep;
1490  t = m_blossomChildren[a_b][j];
1491  if (t >= m_nVertex)
1492  {
1493 #ifdef DEBUG
1494  printf("augmentBlossom 2\n");
1495 #endif
1496  augmentBlossom(t, m_endPoint[p ^ 1]);
1497  }
1498  // Match the edge connecting those sub-blossoms.
1499  m_mate[m_endPoint[p]] = p ^ 1;
1500  m_mate[m_endPoint[p ^ 1]] = p;
1501 #ifdef DEBUG
1502  printf("PAIR %d %d (k=%d)\n", m_endPoint[p], m_endPoint[p ^ 1], p / 2);
1503 #endif
1504  }
1505  // Rotate the list of sub-blossoms to put the new base at the front.
1506  m_blossomChildren[a_b].rotate(i);
1507 #ifdef DEBUG
1508  printf("rotate blossomchilds[%d]=%s\n", a_b, TS_AS_STRING(m_blossomChildren[a_b].ToVecInt()));
1509 #endif
1510  m_blossomEndPts[a_b].rotate(i);
1512  XM_ASSERT(m_blossomBase[a_b] == a_v);
1513 } // MeWeightMatcherImpl::augmentBlossom
1514 //------------------------------------------------------------------------------
1519 //------------------------------------------------------------------------------
1520 void MeWeightMatcherImpl::augmentMatching(int a_k)
1521 {
1522  const MeEdge& edge = GetEdge(a_k);
1523  int v = edge.m_f0;
1524  int w = edge.m_f1;
1525 #ifdef DEBUG
1526  printf("augmentMatching(%d) (v=%d w=%d)\n", a_k, v, w);
1527  printf("PAIR %d %d (k=%d)\n", v, w, a_k);
1528 #endif
1529  VecInt2d vwkTemp = {{v, 2 * a_k + 1}, {w, 2 * a_k}};
1530  VecInt2dPy vwk(vwkTemp);
1531  for (int idx = 0; idx < vwk.size(); ++idx)
1532  {
1533  auto& sp = vwk[idx];
1534  int s = sp[0];
1535  int p = sp[1];
1536 
1537  // Match vertex s to remote endpoint p. Then trace back from s
1538  // until we find a single vertex, swapping matched and unmatched
1539  // edges as we go.
1540  while (true)
1541  {
1542  int bs = m_inBlossom[s];
1543  XM_ASSERT(m_label[bs] == 1);
1545 
1546  // Augment through the S-blossom from s to base.
1547  if (bs >= m_nVertex)
1548  {
1549  augmentBlossom(bs, s);
1550  }
1551  // Update m_mate[s]
1552  m_mate[s] = p;
1553  // Trace one step back.
1554  if (m_labelEnd[bs] == -1)
1555  {
1556  // Reached single vertex; stop.
1557  break;
1558  }
1559  int t = m_endPoint[m_labelEnd[bs]];
1560  int bt = m_inBlossom[t];
1561  XM_ASSERT(m_label[bt] == 2);
1562  // Trace one step back.
1563  XM_ASSERT(m_labelEnd[bt] >= 0);
1564  s = m_endPoint[m_labelEnd[bt]];
1565  int j = m_endPoint[m_labelEnd[bt] ^ 1];
1566  // Augment through the T-blossom from j to base.
1567  XM_ASSERT(m_blossomBase[bt] == t);
1568  if (bt >= m_nVertex)
1569  {
1570  augmentBlossom(bt, j);
1571  }
1572  // Update m_mate[j]
1573  m_mate[j] = m_labelEnd[bt];
1574  // Keep the opposite endpoint;
1575  // it will be assigned to m_mate[s] in the next step.
1576  p = m_labelEnd[bt] ^ 1;
1577 #ifdef DEBUG
1578  printf("PAIR %d %d (k=%d)\n", s, t, p / 2);
1579 #endif
1580  }
1581  }
1582 } // MeWeightMatcherImpl::augmentMatching
1583 //------------------------------------------------------------------------------
1586 //------------------------------------------------------------------------------
1587 void MeWeightMatcherImpl::verifyOptimum(bool a_maxCardinality)
1588 {
1589  int vdualoffset = 0;
1590  if (a_maxCardinality)
1591  {
1592  // Vertices may have negative dual;
1593  // find a constant non-negative number to add to all vertex duals.
1594  vdualoffset = std::max(0, -m_dualVar.min(0, m_nVertex));
1595 #if _DEBUG
1596  XM_ASSERT(m_dualVar.min(0, m_nVertex) + vdualoffset >= 0);
1597  XM_ASSERT(m_dualVar.min(m_nVertex, m_dualVar.size()) >= 0);
1598 #endif
1599  }
1600  // 0. all dual variables are non-negative
1601  // 0. all edges have non-negative slack and
1602  // 1. all matched edges have zero slack;
1603  for (int k = 0; k < m_edges.size(); ++k)
1604  {
1605  const MeEdge& edge = GetEdge(k);
1606  int i = edge.m_f0;
1607  int j = edge.m_f1;
1608  int wt = edge.m_weight;
1609  int s = m_dualVar[i] + m_dualVar[j] - 2 * wt;
1610  VecIntPy iblossoms(1, i);
1611  VecIntPy jblossoms(1, j);
1612  while (m_blossomParent[iblossoms.back()] != -1)
1613  {
1614  iblossoms.push_back(m_blossomParent[iblossoms.back()]);
1615  }
1616  while (m_blossomParent[jblossoms.back()] != -1)
1617  {
1618  jblossoms.push_back(m_blossomParent[jblossoms.back()]);
1619  }
1620  iblossoms.reverse();
1621  jblossoms.reverse();
1622  int ijmin = (int)std::min(iblossoms.size(), jblossoms.size());
1623  for (int ij = 0; ij < ijmin; ++ij)
1624  {
1625  int bi = iblossoms[ij];
1626  int bj = jblossoms[ij];
1627  if (bi != bj)
1628  {
1629  break;
1630  }
1631  s += 2 * m_dualVar[bi];
1632  }
1633  XM_ASSERT(s >= 0);
1634  int mate_i = m_mate[i];
1635  mate_i = mate_i == -1 ? -1 : mate_i / 2;
1636  int mate_j = m_mate[j];
1637  mate_j = mate_j == -1 ? -1 : mate_j / 2;
1638  if (mate_i == k || mate_j == k)
1639  {
1640  XM_ASSERT(mate_i == k && mate_j == k);
1641  XM_ASSERT(s == 0);
1642  }
1643  }
1644  // 2. all single vertices have zero dual value;
1645  for (int v = 0; v < m_nVertex; ++v)
1646  {
1647  XM_ASSERT(m_mate[v] >= 0 || m_dualVar[v] + vdualoffset == 0);
1648  }
1649  // 3. all blossoms with positive dual value are full.
1650  for (int b = m_nVertex; b < 2 * m_nVertex; ++b)
1651  {
1652 #if _DEBUG
1653  if (m_blossomBase[b] >= 0 && m_dualVar[b] > 0)
1654  {
1655  VecIntPy& bendps = m_blossomEndPts[b];
1656  XM_ASSERT(bendps.size() % 2 == 1);
1657  for (int ip = 1; ip < bendps.size(); ip += 2)
1658  {
1659  int p = bendps[ip];
1660  XM_ASSERT(m_mate[m_endPoint[p]] == (p ^ 1));
1661  XM_ASSERT(m_mate[m_endPoint[p ^ 1]] == p);
1662  }
1663  }
1664 #endif
1665  }
1666 } // MeWeightMatcherImpl::verifyOptimum
1667 //------------------------------------------------------------------------------
1669 //------------------------------------------------------------------------------
1670 void MeWeightMatcherImpl::checkDelta2()
1671 {
1672  for (int v = 0; v < m_nVertex; ++v)
1673  {
1674  if (m_label[m_inBlossom[v]] == 0)
1675  {
1676  int bd = 0xdeadc0de; // doesn't get used until after first time through loop
1677  int* pbd = NULL;
1678  int bk = -1;
1679  for (int idx = 0; idx < m_neighbEnd[v].size(); ++idx)
1680  {
1681  int p = m_neighbEnd[v][idx];
1682  int k = p / 2;
1683  int w = m_endPoint[p];
1684  if (m_label[m_inBlossom[w]] == 1)
1685  {
1686  int d = slack(k);
1687  if (bk == -1 || (pbd != NULL && d < *pbd))
1688  {
1689  bk = k;
1690  pbd = &bd;
1691  bd = d;
1692  }
1693  }
1694  }
1695 #ifdef DEBUG
1696  int bev = m_bestEdge[v];
1697  int sbev = slack(bev);
1698  if ((bev != -1 || bk != -1) && (bev == -1 || (pbd != NULL && *pbd != sbev)))
1699  {
1700  printf("v=%d bk=%d bd=%s bestedge=%d slack=%d\n", v, bk, pbd ? TS_AS_STRING(*pbd) : "NULL",
1701  bev, sbev);
1702  }
1703  XM_ASSERT((bk == -1 && bev == -1) || (bev != -1 && bd == sbev));
1704 #endif
1705  }
1706  }
1707 } // MeWeightMatcherImpl::checkDelta2
1708 //------------------------------------------------------------------------------
1710 //------------------------------------------------------------------------------
1711 void MeWeightMatcherImpl::checkDelta3()
1712 {
1713 #ifdef DEBUG
1714  printf("checkDelta3\n");
1715 #endif
1716  int bk = -1;
1717  int bd = 0xbaadf00d; // doesn't get used until after first time through loop
1718  int* pbd = NULL;
1719  int tbk = -1;
1720  int tbd = 0xbaadf00d; // doesn't get used until after first time through loop
1721  int* ptbd = NULL;
1722  for (int b = 0; b < 2 * m_nVertex; ++b)
1723  {
1724  if (m_blossomParent[b] == -1 && m_label[b] == 1)
1725  {
1726  VecIntPy bleaves = blossomLeaves(b);
1727  for (int idx = 0; idx < bleaves.size(); ++idx)
1728  {
1729  int v = bleaves[idx];
1730  for (int idx2 = 0; idx2 < m_neighbEnd[v].size(); ++idx2)
1731  {
1732  int p = m_neighbEnd[v][idx2];
1733  int k = p / 2;
1734  int w = m_endPoint[p];
1735  if (m_inBlossom[w] != b && m_label[m_inBlossom[w]] == 1)
1736  {
1737  int d = slack(k);
1738  if (bk == -1 || (pbd != NULL && d < *pbd))
1739  {
1740  bk = k;
1741  pbd = &bd;
1742  bd = d;
1743 #ifdef DEBUG
1744  printf("changed bd b=%d,v=%d,p=%d,k=%d,bd=%d\n", b, v, p, k, bd);
1745 #endif
1746  }
1747  }
1748  }
1749  }
1750  if (m_bestEdge[b] != -1)
1751  {
1752 #if _DEBUG
1753  const MeEdge& edge = GetEdge(m_bestEdge[b]);
1754  int i = edge.m_f0;
1755  int j = edge.m_f1;
1756  XM_ASSERT(m_inBlossom[i] == b || m_inBlossom[j] == b);
1757  XM_ASSERT(m_inBlossom[i] != b || m_inBlossom[j] != b);
1758  XM_ASSERT(m_label[m_inBlossom[i]] == 1 && m_label[m_inBlossom[j]] == 1);
1759 #endif
1760  if (tbk == -1 || (ptbd != NULL && slack(m_bestEdge[b]) < *ptbd))
1761  {
1762  tbk = m_bestEdge[b];
1763  ptbd = &tbd;
1764  tbd = slack(m_bestEdge[b]);
1765 #ifdef DEBUG
1766  printf("changed tbd b=%d,tbk=%d,tbd=%d\n", b, tbk, tbd);
1767 #endif
1768  }
1769  }
1770  }
1771  }
1772 #ifdef DEBUG
1773  if (bd != tbd)
1774  {
1775  printf("bk=%d tbk=%d bd=%s tbd=%s\n", bk, tbk, pbd ? TS_AS_STRING(*pbd) : "NULL",
1776  ptbd ? TS_AS_STRING(*ptbd) : "NULL");
1777  }
1778 #endif
1779  XM_ASSERT(bd == tbd);
1780 } // MeWeightMatcherImpl::checkDelta3
1781 //------------------------------------------------------------------------------
1788 //------------------------------------------------------------------------------
1789 const MeEdge& MeWeightMatcherImpl::GetEdge(int a_idx)
1790 {
1791  if (a_idx < 0)
1792  {
1793  a_idx = (int)m_edges.size() + a_idx;
1794  }
1795  XM_ASSERT(a_idx >= 0 && a_idx < (int)m_edges.size());
1796  return m_edges[a_idx];
1797 } // MeWeightMatcherImpl::GetEdge
1798 
1799 } // namespace
1800 
1805 //------------------------------------------------------------------------------
1808 //------------------------------------------------------------------------------
1809 BSHP<MeWeightMatcher> MeWeightMatcher::New()
1810 {
1811  BSHP<MeWeightMatcher> wm(new MeWeightMatcherImpl);
1812  return wm;
1813 } // MeWeightMatcher::New
1814 //------------------------------------------------------------------------------
1816 //------------------------------------------------------------------------------
1818 {
1819 } // MeWeightMatcher::MeWeightMatcher
1820 //------------------------------------------------------------------------------
1822 //------------------------------------------------------------------------------
1824 {
1825 } // MeWeightMatcher::~MeWeightMatcher
1826 
1827 } // namespace xms
1828 
1829 #if CXX_TEST
1830 // UNIT TESTS
1833 
1835 
1836 #include <xmscore/testing/TestTools.h>
1838 
1839 //----- Namespace declaration --------------------------------------------------
1840 
1841 using namespace xms;
1842 
1843 namespace
1844 {
1846 #define TS_ASSERT_MATCH_WEIGHTS(a_expected, a_edges, a_cardinality) \
1847  _TS_ASSERT_MATCH_WEIGHTS(__FILE__, __LINE__, a_expected, a_edges, a_cardinality)
1848 #define _TS_ASSERT_MATCH_WEIGHTS(a_file, a_line, a_expected, a_edges, a_cardinality) \
1850  iMatchWeights(a_file, a_line, a_expected, a_edges, a_cardinality)
1851 
1852 //------------------------------------------------------------------------------
1859 //------------------------------------------------------------------------------
1860 void iMatchWeights(const char* a_file,
1861  int a_line,
1862  const VecInt& a_expected,
1863  const VecInt2d& a_edges,
1864  bool a_useMaxCardinality = false)
1865 {
1866  VecMeEdge edges;
1867  for (auto& edgeVec : a_edges)
1868  {
1869  MeEdge edge(edgeVec[0], edgeVec[1], edgeVec[2]);
1870  edges.push_back(edge);
1871  }
1872 
1873  MeWeightMatcherImpl weightMatcher;
1874  VecInt matchWeights = weightMatcher.MatchWeights(edges, a_useMaxCardinality);
1875  _TS_ASSERT_EQUALS(a_file, a_line, a_expected, matchWeights);
1876 } // iTestMatchWeights
1877 
1878 } // namespace
1883 //------------------------------------------------------------------------------
1885 //------------------------------------------------------------------------------
1887 {
1888  VecInt vals = {1, 5, -1, 1, 2, 3, 4};
1889  VecIntPy values(vals);
1890  TS_ASSERT_EQUALS(4, values.back());
1891  TS_ASSERT_EQUALS(3, values[-2]);
1892 
1893  auto min = values.min();
1894  TS_ASSERT_EQUALS(-1, min);
1895 
1896  min = values.min(1, 3);
1897  TS_ASSERT_EQUALS(-1, min);
1898 
1899  auto max = values.max();
1900  TS_ASSERT_EQUALS(5, max);
1901 
1902  max = values.max(2, 6);
1903  TS_ASSERT_EQUALS(3, max);
1904 
1905  values.rotate(4);
1906  VecInt expected = {2, 3, 4, 1, 5, -1, 1};
1907  TS_ASSERT_EQUALS(expected, values.ToVecInt());
1908 
1909  auto index = values.index(1);
1910  TS_ASSERT_EQUALS(3, index);
1911 
1912  values.resize_to_count(7);
1913  expected = {0, 1, 2, 3, 4, 5, 6};
1914  TS_ASSERT_EQUALS(expected, values.ToVecInt());
1915 
1916  values.resize_to_count(4, 5);
1917  expected = {5, 6, 7, 8};
1918  TS_ASSERT_EQUALS(expected, values.ToVecInt());
1919 
1920  values.reverse();
1921  expected = {8, 7, 6, 5};
1922  TS_ASSERT_EQUALS(expected, values.ToVecInt());
1923 } // MeWeightMatcherUnitTests::testPythonVectors
1924 //------------------------------------------------------------------------------
1926 //------------------------------------------------------------------------------
1928 {
1929 #ifdef DEBUG
1930  printf("test10_empty\n\n");
1931 #endif
1932  TS_ASSERT_MATCH_WEIGHTS({}, {}, false);
1933 } // MeWeightMatcherUnitTests::test10_empty
1934 //------------------------------------------------------------------------------
1936 //------------------------------------------------------------------------------
1938 {
1939 #ifdef DEBUG
1940  printf("test11_singleedge\n\n");
1941 #endif
1942  VecInt2d edges = {{0, 1, 1}};
1943  VecInt expected = {1, 0};
1944  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
1945 } // MeWeightMatcherUnitTests::test11_singleedge
1946 //------------------------------------------------------------------------------
1948 //------------------------------------------------------------------------------
1950 {
1951 #ifdef DEBUG
1952  printf("test12\n\n");
1953 #endif
1954  VecInt2d edges = {{1, 2, 10}, {2, 3, 11}};
1955  VecInt expected = {-1, -1, 3, 2};
1956  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
1957 } // MeWeightMatcherUnitTests::test12
1958 //------------------------------------------------------------------------------
1960 //------------------------------------------------------------------------------
1962 {
1963 #ifdef DEBUG
1964  printf("test13\n\n");
1965 #endif
1966  VecInt2d edges = {{1, 2, 5}, {2, 3, 11}, {3, 4, 5}};
1967  VecInt expected = {-1, -1, 3, 2, -1};
1968  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
1969 } // MeWeightMatcherUnitTests::test13
1970 //------------------------------------------------------------------------------
1972 //------------------------------------------------------------------------------
1974 {
1975 #ifdef DEBUG
1976  printf("test14_maxcard\n\n");
1977 #endif
1978  VecInt2d edges = {{1, 2, 5}, {2, 3, 11}, {3, 4, 5}};
1979  VecInt expected = {-1, 2, 1, 4, 3};
1980  TS_ASSERT_MATCH_WEIGHTS(expected, edges, true);
1981 } // MeWeightMatcherUnitTests::test14_maxcard
1982 //------------------------------------------------------------------------------
1984 //------------------------------------------------------------------------------
1986 {
1987 #ifdef DEBUG
1988  printf("test16_negative\n\n");
1989 #endif
1990  VecInt2d edges = {{1, 2, 2}, {1, 3, -2}, {2, 3, 1}, {2, 4, -1}, {3, 4, -6}};
1991  VecInt expected = {-1, 2, 1, -1, -1};
1992  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
1993 #ifdef DEBUG
1994  printf("test16_negative-true\n\n");
1995 #endif
1996  expected = {-1, 3, 4, 1, 2};
1997  TS_ASSERT_MATCH_WEIGHTS(expected, edges, true);
1998 } // MeWeightMatcherUnitTests::test16_negative
1999 //------------------------------------------------------------------------------
2001 //------------------------------------------------------------------------------
2003 {
2004 #ifdef DEBUG
2005  printf("test20_sblossom-1\n\n");
2006 #endif
2007  VecInt2d edges = {{1, 2, 8}, {1, 3, 9}, {2, 3, 10}, {3, 4, 7}};
2008  VecInt expected = {-1, 2, 1, 4, 3};
2009  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2010 #ifdef DEBUG
2011  printf("test20_sblossom-2\n\n");
2012 #endif
2013  edges = {{1, 2, 8}, {1, 3, 9}, {2, 3, 10}, {3, 4, 7}, {1, 6, 5}, {4, 5, 6}};
2014  expected = {-1, 6, 3, 2, 5, 4, 1};
2015  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2016 } // MeWeightMatcherUnitTests::test20_sblossom
2017 //------------------------------------------------------------------------------
2019 //------------------------------------------------------------------------------
2021 {
2022 #ifdef DEBUG
2023  printf("test21_tblossom-1\n\n");
2024 #endif
2025  VecInt2d edges = {{1, 2, 9}, {1, 3, 8}, {2, 3, 10}, {1, 4, 5}, {4, 5, 4}, {1, 6, 3}};
2026  VecInt expected = {-1, 6, 3, 2, 5, 4, 1};
2027  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2028 #ifdef DEBUG
2029  printf("test21_tblossom-2\n\n");
2030 #endif
2031  edges = {{1, 2, 9}, {1, 3, 8}, {2, 3, 10}, {1, 4, 5}, {4, 5, 3}, {1, 6, 4}};
2032  expected = {-1, 6, 3, 2, 5, 4, 1};
2033  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2034 #ifdef DEBUG
2035  printf("test21_tblossom-3\n\n");
2036 #endif
2037  edges = {{1, 2, 9}, {1, 3, 8}, {2, 3, 10}, {1, 4, 5}, {4, 5, 3}, {3, 6, 4}};
2038  expected = {-1, 2, 1, 6, 5, 4, 3};
2039  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2040 } // MeWeightMatcherUnitTests::test21_tblossom
2041 //------------------------------------------------------------------------------
2043 //------------------------------------------------------------------------------
2045 {
2046 #ifdef DEBUG
2047  printf("test22_s_nest\n\n");
2048 #endif
2049  VecInt2d edges = {{1, 2, 9}, {1, 3, 9}, {2, 3, 10}, {2, 4, 8}, {3, 5, 8}, {4, 5, 10}, {5, 6, 6}};
2050  VecInt expected = {-1, 3, 4, 1, 2, 6, 5};
2051  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2052 } // MeWeightMatcherUnitTests::test22_s_nest
2053 //------------------------------------------------------------------------------
2055 //------------------------------------------------------------------------------
2057 {
2058 #ifdef DEBUG
2059  printf("test23_s_relabel_nest\n\n");
2060 #endif
2061  VecInt2d edges = {{1, 2, 10}, {1, 7, 10}, {2, 3, 12}, {3, 4, 20}, {3, 5, 20},
2062  {4, 5, 25}, {5, 6, 10}, {6, 7, 10}, {7, 8, 8}};
2063  VecInt expected = {-1, 2, 1, 4, 3, 6, 5, 8, 7};
2064  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2065 } // MeWeightMatcherUnitTests::test23_s_relabel_nest
2066 //------------------------------------------------------------------------------
2068 //------------------------------------------------------------------------------
2070 {
2071 #ifdef DEBUG
2072  printf("test24_s_nest_expand\n\n");
2073 #endif
2074  VecInt2d edges = {{1, 2, 8}, {1, 3, 8}, {2, 3, 10}, {2, 4, 12}, {3, 5, 12},
2075  {4, 5, 14}, {4, 6, 12}, {5, 7, 12}, {6, 7, 14}, {7, 8, 12}};
2076  VecInt expected = {-1, 2, 1, 5, 6, 3, 4, 8, 7};
2077  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2078 } // MeWeightMatcherUnitTests::test24_s_nest_expand
2079 //------------------------------------------------------------------------------
2081 //------------------------------------------------------------------------------
2083 {
2084 #ifdef DEBUG
2085  printf("test25_s_t_expand\n\n");
2086 #endif
2087  VecInt2d edges = {{1, 2, 23}, {1, 5, 22}, {1, 6, 15}, {2, 3, 25},
2088  {3, 4, 22}, {4, 5, 25}, {4, 8, 14}, {5, 7, 13}};
2089  VecInt expected = {-1, 6, 3, 2, 8, 7, 1, 5, 4};
2090  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2091 } // MeWeightMatcherUnitTests::test25_s_t_expand
2092 //------------------------------------------------------------------------------
2094 //------------------------------------------------------------------------------
2096 {
2097 #ifdef DEBUG
2098  printf("test26_s_nest_t_expand\n\n");
2099 #endif
2100  VecInt2d edges = {{1, 2, 19}, {1, 3, 20}, {1, 8, 8}, {2, 3, 25}, {2, 4, 18},
2101  {3, 5, 18}, {4, 5, 13}, {4, 7, 7}, {5, 6, 7}};
2102  VecInt expected = {-1, 8, 3, 2, 7, 6, 5, 4, 1};
2103  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2104 } // MeWeightMatcherUnitTests::test26_s_nest_t_expand
2105 //------------------------------------------------------------------------------
2108 //------------------------------------------------------------------------------
2110 {
2111 #ifdef DEBUG
2112  printf("test30_tnasty_expand\n\n");
2113 #endif
2114  VecInt2d edges = {{1, 2, 45}, {1, 5, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 50},
2115  {1, 6, 30}, {3, 9, 35}, {4, 8, 35}, {5, 7, 26}, {9, 10, 5}};
2116  VecInt expected = {-1, 6, 3, 2, 8, 7, 1, 5, 4, 10, 9};
2117  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2118 } // MeWeightMatcherUnitTests::test30_tnasty_expand
2119 //------------------------------------------------------------------------------
2121 //------------------------------------------------------------------------------
2123 {
2124 #ifdef DEBUG
2125  printf("test31_tnasty2_expand\n\n");
2126 #endif
2127  VecInt2d edges = {{1, 2, 45}, {1, 5, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 50},
2128  {1, 6, 30}, {3, 9, 35}, {4, 8, 26}, {5, 7, 40}, {9, 10, 5}};
2129  VecInt expected = {-1, 6, 3, 2, 8, 7, 1, 5, 4, 10, 9};
2130  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2131 } // MeWeightMatcherUnitTests::test31_tnasty2_expand
2132 //------------------------------------------------------------------------------
2135 //------------------------------------------------------------------------------
2137 {
2138 #ifdef DEBUG
2139  printf("test32_t_expand_leastslack\n\n");
2140 #endif
2141  VecInt2d edges = {{1, 2, 45}, {1, 5, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 50},
2142  {1, 6, 30}, {3, 9, 35}, {4, 8, 28}, {5, 7, 26}, {9, 10, 5}};
2143  VecInt expected = {-1, 6, 3, 2, 8, 7, 1, 5, 4, 10, 9};
2144  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2145 } // MeWeightMatcherUnitTests::test32_t_expand_leastslack
2146 //------------------------------------------------------------------------------
2149 //------------------------------------------------------------------------------
2151 {
2152 #ifdef DEBUG
2153  printf("test33_nest_tnasty_expand\n\n");
2154 #endif
2155  VecInt2d edges = {{1, 2, 45}, {1, 7, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 95},
2156  {4, 6, 94}, {5, 6, 94}, {6, 7, 50}, {1, 8, 30}, {3, 11, 35},
2157  {5, 9, 36}, {7, 10, 26}, {11, 12, 5}};
2158  VecInt expected = {-1, 8, 3, 2, 6, 9, 4, 10, 1, 5, 7, 12, 11};
2159  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2160 } // MeWeightMatcherUnitTests::test33_nest_tnasty_expand
2161 //------------------------------------------------------------------------------
2163 //------------------------------------------------------------------------------
2165 {
2166 #ifdef DEBUG
2167  printf("test34_nest_relabel_expand\n\n");
2168 #endif
2169  VecInt2d edges = {{1, 2, 40}, {1, 3, 40}, {2, 3, 60}, {2, 4, 55}, {3, 5, 55}, {4, 5, 50},
2170  {1, 8, 15}, {5, 7, 30}, {7, 6, 10}, {8, 10, 10}, {4, 9, 30}};
2171  VecInt expected = {-1, 2, 1, 5, 9, 3, 7, 6, 10, 4, 8};
2172 
2173  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2174 } // MeWeightMatcherUnitTests::test34_nest_relabel_expand
2175 //------------------------------------------------------------------------------
2177 //------------------------------------------------------------------------------
2179 {
2180 #ifdef DEBUG
2181  printf("testSimpleTriangle\n\n");
2182 #endif
2183  // VecPt3d points = { {0, 0, 0}, {10, 0, 0}, {20, 0, 0}, {30, 0, 0},
2184  // {0, 10, 0}, {10, 10, 0}, {20, 10, 0},
2185  // {0, 20, 0}, {10, 20, 0},
2186  // {0, 30, 0}};
2187  // VecInt2d triangles = {{0, 1, 4}, {1, 5, 4}, {1, 2, 5}, {2, 6, 5}, {2, 3, 6},
2188  // {4, 5, 7}, {5, 8, 7}, {5, 6, 8}, {7, 8, 9}};
2189  // clang-format off
2190  VecInt2d edges = { {0, 1, 15}, {1, 2, 10}, {2, 3, 15}, {3, 4, 10},
2191  {1, 5, 10}, {3, 7, 10}, {5, 6, 15}, {6, 7, 10},
2192  {6, 8, 10},
2193  {0, 2, -10}, {2, 4, -10}, {4, 7, -10}, {7, 8, -10}, {8, 5, -10}, {5, 0, -10}};
2194  // clang-format on
2195  VecInt expected = {1, 0, 3, 2, -1, 6, 5, -1, -1};
2196  // 0, 1, 2, 3, 4, 5, 6, 7, 8
2197 
2198  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2199  expected = {1, 0, 3, 2, 7, 6, 5, 4, -1};
2200  // 0, 1, 2, 3, 4, 5, 6, 7, 8
2201  TS_ASSERT_MATCH_WEIGHTS(expected, edges, true);
2202 } // MeWeightMatcherUnitTests::testSimpleTriangle
2203 //------------------------------------------------------------------------------
2205 //------------------------------------------------------------------------------
2207 {
2208 #ifdef DEBUG
2209  printf("testSimpleQuad\n\n");
2210 #endif
2211  // clang-format off
2212  //VecPt3d points = {
2213  // {0, 0, 0}, {10, 0, 0}, {20, 0, 0}, {30, 0, 0},
2214  // {0, 10, 0}, {10, 10, 0}, {20, 10, 0}, {30, 10, 0},
2215  // {0, 20, 0}, {10, 20, 0}, {20, 20, 0}, {30, 20, 0},
2216  // {0, 30, 0}, {10, 30, 0}, {20, 30, 0}, {30, 30, 0}};
2217  //VecInt2d triangles = {
2218  // {0, 1, 4}, {1, 5, 4}, {1, 2, 5}, {2, 6, 5}, {2, 3, 6}, {3, 7, 6},
2219  // {4, 9, 8}, {4, 5, 9}, {5, 10, 9}, {5, 6, 10}, {6, 7, 11}, {6, 11, 10},
2220  // {8, 9, 12}, {9, 13, 12}, {9, 10, 13}, {10, 14, 13}, {10, 11, 14},
2221  // {11, 15, 14}};
2222  VecInt2d edges = {
2223  {0, 1, 15}, {1, 2, 10}, {2, 3, 15}, {3, 4, 10}, {4, 5, 15}, // inside row 1
2224  {6, 7, 15}, {7, 8, 10}, {8, 9, 15}, {9, 10, 10}, {10, 11, 15}, // inside row 2
2225  {12, 13, 15}, {13, 14, 10}, {14, 15, 15}, {15, 16, 10}, {16, 17, 15}, // inside row 3
2226  {0, 2, -10}, {2, 4, -10}, {4, 5, -5}, {5, 11, -10}, {11, 17, -10}, // outside
2227  {17, 15, -10}, {15, 13, -10}, {12, 13, -5}, {12, 6, -10}, {6, 0, -10}
2228  };
2229  // clang-format on
2230 
2231  VecInt expected = {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16};
2232  // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
2233 
2234  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2235  TS_ASSERT_MATCH_WEIGHTS(expected, edges, true);
2236 } // MeWeightMatcherUnitTests::testSimpleQuad
2237 //------------------------------------------------------------------------------
2239 // two on the horizontal bottom row increasing to six divisions on top row.
2240 //------------------------------------------------------------------------------
2242 {
2243 #ifdef DEBUG
2244  printf("testComplexQuad\n\n");
2245 #endif
2246  // clang-format off
2247  //VecPt3d points = {
2248  // {-10, 0, 0}, {0, 0, 0}, {10, 0,0},
2249  // {-15, 10, 0}, {-5, 10, 0}, {5, 10, 0}, {15, 10, 0},
2250  // {-20, 20, 0}, {-10, 20, 0}, {0, 20, 0}, {10, 20, 0}, {20, 20, 0},
2251  // {-25, 30, 0}, {-15, 30, 0}, {-5, 30, 0}, {5, 30, 0}, {15, 30, 0}, {25, 30, 0},
2252  // {-30, 40, 0}, {-20, 40, 0}, {-10, 40, 0}, {0, 40, 0}, {10, 40, 0}, {20, 40, 0}, {30, 40, 0}};
2253  //VecInt2d triangles = {
2254  // {0, 4, 3}, {0, 1, 4}, {1, 5, 4}, {0, 2, 5}, {2, 6, 5},
2255  // {3, 8, 7}, {3, 4, 8}, {4, 9, 8}, {4, 5, 9}, {5, 10, 9}, {5, 11, 10}, {5, 6, 11},
2256  // {7, 13, 12}, {7, 8, 13}, {8, 14, 13}, {8, 9, 14}, {9, 15, 14}, {9, 10, 15}, {10, 16, 15}, {10, 11, 16}, {11, 17, 16},
2257  // {12, 19, 18}, {12, 13, 19}, {13, 20, 19}, {13, 14, 20}, {14, 21, 20}, {14, 15, 21}, {15, 22, 21}, {15, 23, 22}, {15, 16, 23}, {16, 17, 23}, {17, 24, 23}};
2258  VecInt2d edges = {
2259  // vertical triangle edges bottom row
2260  {0, 1, 10}, {1, 2, 10}, {2, 3, 10}, {3, 4, 10},
2261  // horizontal triangle edges bottom row
2262  {0, 6, 10}, {2, 8, 10}, {4, 11, 10},
2263  // vertical triangles second row
2264  {5, 6, 10}, {6, 7, 10}, {7, 8, 10}, {8, 9, 10}, {9, 10, 10}, {10, 11, 10},
2265  // horizontal triangles second row
2266  {5, 13, 10}, {7, 15, 10}, {9, 17, 10}, {10, 19, 10},
2267  // vertical triangles third row
2268  {12, 13, 10}, {13, 14, 10}, {14, 15, 10}, {15, 16, 10}, {16, 17, 10},
2269  {17, 18, 10}, {18, 19, 10}, {19, 20, 10},
2270  // horizontal triangles third row
2271  {12, 22, 10}, {14, 24, 10}, {16, 26, 10}, {18, 29, 10}, {20, 30, 10},
2272  {21, 22, 10}, {22, 23, 10}, {23, 24, 10}, {24, 25, 10}, {25, 26, 10},
2273  {26, 27, 10}, {27, 28, 10}, {28, 29, 10}, {29, 30, 10}, {30, 31, 10},
2274  // boundary edges
2275  {0, 1, -5}, {1, 3, -10}, {3, 4, -5}, {4, 11, -5}, {11, 20, -15}, {20, 31, -10},
2276  {31, 28, -15}, {28, 27, -5}, {27, 25, -10}, {25, 23, -10}, {23, 21, -10}, {21, 12, -10},
2277  {12, 5, -10}, {5, 0, -10}
2278  };
2279 
2280  VecInt expected = { 1, 0, 3, 2, 11, 6, 5, 8, 7, 10, 9, 4, 13, 12, 15, 14, 17, 16, 29, 20, 19, 22, 21, 24, 23, 26, 25, 28, 27, 18, 31, 30 };
2281  // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
2282  // clang-format on
2283 
2284  TS_ASSERT_MATCH_WEIGHTS(expected, edges, false);
2285  TS_ASSERT_MATCH_WEIGHTS(expected, edges, true);
2286 } // MeWeightMatcherUnitTests::testComplexQuad
2287 
2288 #endif // CXX_TEST
#define TS_ASSERT_MATCH_WEIGHTS(a_expected, a_edges, a_cardinality)
Helper to run matcher tests.
std::vector< int > VecInt
void testSimpleQuad()
Test simple quad with three divisions on each side.
std::vector< bool > VecBool
static BSHP< MeWeightMatcher > New()
Create new weight matcher.
void test14_maxcard()
Test maximum cardinality.
void test21_tblossom()
Test create S-blossom, relabel as T-blossom, use for augmentation.
VecInt2dPy m_blossomChildren
void test32_t_expand_leastslack()
Test create blossom, relabel as T, expand such that a new least-slack S-to-free edge is produced...
void test25_s_t_expand()
Test create S-blossom, relabel as T, expand.
int m_weight
The weight assigned to the edge.
VecInt2dPy m_blossomBestEdges
int m_nVertex
Number of points in the mesh.
void testSimpleTriangle()
Test simple triangle with three divisions on each side.
VecIntPy m_labelEnd
void test34_nest_relabel_expand()
Test create nested S-blossom, relabel as S, expand recursively.
VecIntPy m_inBlossom
VecIntPy m_queue
Queue of newly discovered S-vertices.
void test11_singleedge()
Test single edge.
VecMeEdge m_edges
Edges are numbered 0 .. (nedge-1).
std::vector< VecInt > VecInt2d
VecIntPy m_mate
void test12()
Test with two edges.
VecInt m_vec
The vector storage.
VecIntPy m_blossomParent
void test20_sblossom()
Test create S-blossom and use it for augmentation.
MeWeightMatcher()
Constructor.
VecIntPy m_unusedBlossoms
void test13()
Test with three edges.
void test33_nest_tnasty_expand()
Test create nested blossom, relabel as T in more than one way, expand outer blossom such that inner b...
void testPythonVectors()
Test VecIntPy class.
void test24_s_nest_expand()
Test create nested S-blossom, augment, expand recursively.
#define XM_ASSERT(x)
VecInt2dPy m_neighbEnd
VecIntPy m_bestEdge
int m_nEdge
Number of edges in m_edges.
void test31_tnasty2_expand()
Test again but slightly different.
void test30_tnasty_expand()
Test create blossom, relabel as T in more than one way, expand, augment.
void test10_empty()
Test empty input graph.
int m_maxWeight
Maximum of weights in m_edges.
void test26_s_nest_t_expand()
Test create nested S-blossom, relabel as T, expand.
int m_f1
Index of one of the adjacent faces.
VecBool m_allowEdge
void testComplexQuad()
Test complex quad with three divisions on two opposite sides and.
VecIntPy m_label
VecIntPy m_endPoint
void test23_s_relabel_nest()
Test create S-blossom, relabel as S, include in nested S-blossom.
VecIntPy m_blossomBase
int m_f0
Index of one of the adjacent faces.
VecInt2dPy m_blossomEndPts
std::vector< MeEdge > VecMeEdge
Vector of MeEdge.
VecIntPy m_dualVar
void test22_s_nest()
Test create nested S-blossom, use for augmentation.
void test16_negative()
Test negative weights.
virtual ~MeWeightMatcher()
Destructor.