Limbo
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FM.h
Go to the documentation of this file.
1 
12 #ifndef _LIMBO_ALGORITHMS_PARTITION_FM_H
13 #define _LIMBO_ALGORITHMS_PARTITION_FM_H
14 
15 #include <iostream>
16 #include <vector>
17 #include <map>
18 #include <set>
19 #include <limbo/math/Math.h>
20 #include <boost/array.hpp>
21 #include <boost/unordered_map.hpp>
22 
23 using std::cout;
24 using std::endl;
25 using std::vector;
26 using std::map;
27 using std::set;
28 using std::pair;
29 using std::make_pair;
30 using boost::array;
31 using boost::unordered_map;
32 
34 namespace limbo
35 {
37 namespace algorithms
38 {
40 namespace partition
41 {
42 
46 template <typename NodeType>
48 {
50  typedef NodeType node_type;
51  typedef typename node_type::tie_id_type tie_id_type;
52  typedef typename node_type::weight_type node_weight_type;
54 
57  static tie_id_type tie_id(node_type const& n)
58  {return n.tie_id();}
61  static node_weight_type weight(node_type const& n)
62  {return n.weight();}
63 };
64 
71 template <typename NodeType, typename NetWeightType = double>
72 class FM
73 {
74  public:
76  typedef NodeType node_type;
77  typedef NetWeightType net_weight_type;
78  typedef typename FM_node_traits<node_type>::node_weight_type node_weight_type;
80 
81  struct FM_node_type;
82  struct FM_net_type;
83 
86  struct FM_node_type
87  {
88  node_type* pNode;
89  vector<FM_net_type*> vNet;
90  bool locked;
91  int partition;
93  node_weight_type weight;
94 
97  {
98  pNode = NULL;
99  locked = false;
100  partition = -1;
101  partition_bak = -1;
102  weight = 0;
103  }
106  net_weight_type gain() const
107  {
108  net_weight_type g = 0;
109  for (typename vector<FM_net_type*>::const_iterator itNet = vNet.begin();
110  itNet != vNet.end(); ++itNet)
111  {
112 #if 0
113  for (typename vector<FM_node_type*>::const_iterator itNode = (*itNet)->vNode.begin();
114  itNode != (*itNet)->vNode.end(); ++itNode)
115  {
116  assert((*itNode)->partition == 0 || (*itNode)->partition == 1);
117 
118  if (this == *itNode) continue;
119  else if (this->partition == (*itNode)->partition) // internal node
120  g -= (*itNet)->weight;
121  else // external node
122  g += (*itNet)->weight;
123  }
124 #else
125  int exclusivePartition = (*itNet)->exclusive_partition(*this);
126  if (exclusivePartition != 2)
127  {
128  // current node is in the same partition as other nodes in this net
129  if (this->partition == exclusivePartition)
130  g -= (*itNet)->weight;
131  // current node is in different partitions from other nodes in this net
132  else
133  g += (*itNet)->weight;
134  }
135 #endif
136  }
137  return g;
138  }
139  };
142  struct FM_net_type
143  {
144  vector<FM_node_type*> vNode;
145  net_weight_type weight;
146 
149  net_weight_type cutsize() const
150  {
151  if (vNode.size() < 2) return 0;
152  for (typename vector<FM_node_type*>::const_iterator itNode = vNode.begin()+1;
153  itNode != vNode.end(); ++itNode)
154  {
155  assert((*itNode)->partition == 0 || (*itNode)->partition == 1);
156  if ((*(itNode-1))->partition != (*itNode)->partition) // has a cut
157  return weight;
158  }
159  return 0;
160  }
166  int exclusive_partition(FM_node_type const& fmNode) const
167  {
168  int prevPartition = -1;
169  int exclusivePartition = -1; // except current node,
170  // 0 indicates all in partition 0,
171  // 1 indicates all in partition 1,
172  // 2 indicates in both partitions
173  for (typename vector<FM_node_type*>::const_iterator itNode = vNode.begin();
174  itNode != vNode.end(); ++itNode)
175  {
176  assert((*itNode)->partition == 0 || (*itNode)->partition == 1);
177 
178  if (fmNode.pNode == (*itNode)->pNode) continue;
179  else if (prevPartition < 0)
180  {
181  prevPartition = (*itNode)->partition;
182  exclusivePartition = prevPartition;
183  }
184  else if (prevPartition != (*itNode)->partition)
185  {
186  exclusivePartition = 2;
187  break;
188  }
189  }
190  return exclusivePartition;
191  }
192  };
193 
194  // forward declaration
195  struct compare_type1;
196  struct compare_type2;
201  {
206  bool operator()(FM_node_type* pFMNode1, FM_node_type* pFMNode2) const
207  {
208  return pFMNode1->gain() > pFMNode2->gain();
209  }
214  bool operator()(net_weight_type const& g1, net_weight_type const& g2) const
215  {
216  return g1 > g2;
217  }
218 #if 0
219  bool operator()(set<FM_node_type*, compare_type2> const& s1, set<FM_node_type*, compare_type2> const& s2) const
220  {
221  assert(!s1.empty() && !s2.empty());
222  return (*this)(*s1.begin(), *s2.begin());
223  }
224 #endif
225  };
230  {
235  bool operator()(FM_node_type* pFMNode1, FM_node_type* pFMNode2) const
236  {
238  }
239  };
240 
243  class gain_bucket_type : public map<net_weight_type, set<FM_node_type*, compare_type2>, compare_type1>
244  {
245  public:
247  typedef set<FM_node_type*, compare_type2> nested_type;
248  typedef map<net_weight_type, nested_type, compare_type1> base_type;
250 
252  gain_bucket_type() : base_type() {}
255  gain_bucket_type(gain_bucket_type const& rhs) : base_type(rhs) {}
256 
259  virtual void insert(FM_node_type* const& pFMNode)
260  {
261  net_weight_type gain = pFMNode->gain();
262  typename base_type::iterator found = this->find(gain);
263  if (found == this->end())
264  {
265  set<FM_node_type*, compare_type2> sTmp;
266  sTmp.insert(pFMNode);
267  this->base_type::insert(make_pair(gain, sTmp));
268  }
269  else
270  {
271  found->second.insert(pFMNode);
272  }
273  }
276  virtual void erase(FM_node_type* const& pFMNode)
277  {
278  net_weight_type gain = pFMNode->gain();
279  typename base_type::iterator found = this->find(gain);
280  if (found != this->end())
281  {
282  if (found->second.empty()) return;
283  else if (found->second.size() == 1 && found->second.count(pFMNode))
284  this->base_type::erase(found);
285  else
286  found->second.erase(pFMNode);
287  }
288  }
291  size_t element_size() const
292  {
293  size_t cnt = 0;
294  for (typename base_type::const_iterator it1 = this->begin();
295  it1 != this->end(); ++it1)
296  for (typename nested_type::const_iterator it2 = it1->second.begin();
297  it2 != it1->second.end(); ++it2)
298  cnt += 1;
299  return cnt;
300  }
302  void print() const
303  {
304  cout << "------- gain_bucket -------" << endl;
305  cout << "total # element = " << this->element_size() << endl;
306  for (typename base_type::const_iterator it1 = this->begin();
307  it1 != this->end(); ++it1)
308  {
309  cout << "gain = " << it1->first << ": ";
310  for (typename nested_type::const_iterator it2 = it1->second.begin();
311  it2 != it1->second.end(); ++it2)
312  cout << FM_node_traits<node_type>::tie_id(*((*it2)->pNode)) << " ";
313  cout << endl;
314  }
315  }
316  };
317 
319  FM() {}
321  ~FM()
322  {
323  for (typename unordered_map<node_type*, FM_node_type*>::const_iterator it = m_hNode.begin();
324  it != m_hNode.end(); ++it)
325  delete it->second;
326  for (typename vector<FM_net_type*>::const_iterator it = m_vNet.begin();
327  it != m_vNet.end(); ++it)
328  delete *it;
329  }
334  bool add_node(node_type* pNode, int initialPartition)
335  {
336  assert(initialPartition == 0 || initialPartition == 1);
337 
338  FM_node_type* pFMNode = new FM_node_type;
339  pFMNode->pNode = pNode;
340  pFMNode->partition = initialPartition;
341  pFMNode->weight = FM_node_traits<node_type>::weight(*pNode);
342  return m_hNode.insert(make_pair(pNode, pFMNode)).second;
343  }
353  template <typename Iterator>
354  bool add_net(net_weight_type const& weight, Iterator first, Iterator last)
355  {
356  FM_net_type* pFMNet = new FM_net_type;
357  pFMNet->weight = weight;
358 
359  for (Iterator it = first; it != last; ++it)
360  {
361  typename unordered_map<node_type*, FM_node_type*>::const_iterator found = m_hNode.find(*it);
362  // return false if failed
363  if (found == m_hNode.end())
364  {
365  delete pFMNet;
366  return false;
367  }
368  // add FM_node_type to FM_net_type
369  pFMNet->vNode.push_back(found->second);
370  }
371  // add FM_net_type to FM_node_type
372  for (typename vector<FM_node_type*>::const_iterator itNode = pFMNet->vNode.begin();
373  itNode != pFMNet->vNode.end(); ++itNode)
374  (*itNode)->vNet.push_back(pFMNet);
375  m_vNet.push_back(pFMNet);
376 
377  return true;
378  }
380  net_weight_type cutsize() const
381  {
382  net_weight_type cs = 0;
383  for (typename vector<FM_net_type*>::const_iterator it = m_vNet.begin();
384  it != m_vNet.end(); ++it)
385  cs += (*it)->cutsize();
386  return cs;
387  }
392  net_weight_type operator()(double ratio1, double ratio2)
393  {
394  return this->run(ratio1, ratio2);
395  }
397  void print() const
398  {
399  cout << "------- partitions -------" << endl;
400  vector<typename FM_node_traits<node_type>::tie_id_type> vPartition[2];
401 
402  for (typename unordered_map<node_type*, FM_node_type*>::const_iterator it = m_hNode.begin();
403  it != m_hNode.end(); ++it)
404  {
405  FM_node_type* const& pFMNode = it->second;
406  assert(pFMNode->partition == 0 || pFMNode->partition == 1);
407  vPartition[pFMNode->partition].push_back(FM_node_traits<node_type>::tie_id(*(pFMNode->pNode)));
408  }
409  cout << "{";
410  for (typename vector<typename FM_node_traits<node_type>::tie_id_type>::const_iterator it = vPartition[0].begin();
411  it != vPartition[0].end(); ++it)
412  cout << (*it) << " ";
413  cout << "| ";
414  for (typename vector<typename FM_node_traits<node_type>::tie_id_type>::const_iterator it = vPartition[1].begin();
415  it != vPartition[1].end(); ++it)
416  cout << (*it) << " ";
417  cout << "}" << endl;
418  }
420  void print_connection() const
421  {
422  cout << "------- connections -------" << endl;
423  for (typename vector<FM_net_type*>::const_iterator itNet = m_vNet.begin();
424  itNet != m_vNet.end(); ++itNet)
425  {
426  FM_net_type* const& pFMNet = *itNet;
427  for (typename vector<FM_node_type*>::const_iterator itNode = pFMNet->vNode.begin();
428  itNode != pFMNet->vNode.end(); ++itNode)
429  {
430  // delimiter
431  if (itNode != pFMNet->vNode.begin())
432  cout << " - ";
433  FM_node_type* const& pFMNode = *itNode;
434  cout << FM_node_traits<node_type>::tie_id(*(pFMNode->pNode));
435  }
436  cout << endl;
437  }
438  }
440  void print_node() const
441  {
442  cout << "------- nodes -------" << endl;
443  for (typename unordered_map<node_type*, FM_node_type*>::const_iterator it = m_hNode.begin();
444  it != m_hNode.end(); ++it)
445  {
446  FM_node_type* const& pFMNode = it->second;
447  assert(pFMNode->partition == 0 || pFMNode->partition == 1);
448 
449  cout << FM_node_traits<node_type>::tie_id(*(pFMNode->pNode)) << ": ";
450  cout << "partition = " << pFMNode->partition << "; ";
451  cout << "partition_bak = " << pFMNode->partition_bak << "; ";
452  cout << "locked = " << pFMNode->locked << "; ";
453  cout << "weight = " << pFMNode->weight << "; ";
454  cout << "gain = " << pFMNode->gain() << "; ";
455  cout << endl;
456  }
457  }
458  protected:
463  net_weight_type run(double ratio1, double ratio2)
464  {
465  net_weight_type prev_cutsize;
466  net_weight_type cur_cutsize = this->cutsize();
467 
468  unsigned int iter_cnt = 0;
469  do
470  {
471  cout << "######### iteration #" << iter_cnt++ << " #########" << endl;
472 
473  prev_cutsize = cur_cutsize;
474 
475  pair<net_weight_type, int> trial_pass = this->single_pass(ratio1, ratio2, -1);
476 #ifdef DEBUG_FM
477  //this->print_node();
478 #endif
479  pair<net_weight_type, int> actual_pass = this->single_pass(ratio1, ratio2, trial_pass.second);
480 
481 #ifdef DEBUG_FM
482  this->print_node();
483  assert(trial_pass == actual_pass && limbo::abs(actual_pass.first-this->cutsize()) < 1e-6);
484 #endif
485 
486  cur_cutsize = actual_pass.first;
487  } while (cur_cutsize < prev_cutsize);
488 
489  return cur_cutsize;
490  }
496  pair<net_weight_type, int> single_pass(double ratio1, double ratio2, int target_cnt)
497  {
498  // trial mode, back up partition
499  if (target_cnt < 0)
500  {
501  cout << "======= trial phase =======" << endl;
502  for (typename unordered_map<node_type*, FM_node_type*>::const_iterator it = m_hNode.begin();
503  it != m_hNode.end(); ++it)
504  {
505  FM_node_type* const& pFMNode = it->second;
506  pFMNode->partition_bak = pFMNode->partition;
507  }
508  }
509  else cout << "======= actual phase w. target_cnt = " << target_cnt << " =======" << endl;
510 #ifdef DEBUG_FM
511  //this->print_node();
512 #endif
513  // initialize gain_bucket
514  gain_bucket_type gain_bucket;
515  node_weight_type total_weight[2] = {0, 0};
516  net_weight_type cur_cutsize = this->cutsize();
517  net_weight_type best_cutsize = cur_cutsize;
518  for (typename unordered_map<node_type*, FM_node_type*>::const_iterator it = m_hNode.begin();
519  it != m_hNode.end(); ++it)
520  {
521  FM_node_type* const& pFMNode = it->second;
522  assert(pFMNode->partition == 0 || pFMNode->partition == 1);
523  total_weight[pFMNode->partition] += pFMNode->weight;
524  gain_bucket.insert(pFMNode);
525  }
526 
527 #ifdef DEBUG_FM
528  // for Physical Design HW 2.b)
529  gain_bucket.print();
530 #endif
531 
532  for (typename unordered_map<node_type*, FM_node_type*>::const_iterator it = m_hNode.begin();
533  it != m_hNode.end(); ++it)
534  {
535  FM_node_type* const& pFMNode = it->second;
536  pFMNode->locked = false;
537  }
538 
539 #ifdef DEBUG_FM
540  this->print();
541  cout << "initial cutsize = " << this->cutsize() << endl;
542 #endif
543  // 1 trial pass, 1 real pass
544  int cur_cnt = 0;
545  int best_cnt = 0;
546  while (!gain_bucket.empty())
547  {
548  if (cur_cnt == target_cnt) break;
549 #ifdef DEBUG_FM
550  //this->print_node();
551 #endif
552  FM_node_type* pFMNodeBest = NULL; // candidnate node to move
553 
554  for (typename gain_bucket_type::const_iterator it1 = gain_bucket.begin();
555  it1 != gain_bucket.end(); ++it1)
556  {
557  for (typename gain_bucket_type::nested_type::const_iterator it2 = it1->second.begin();
558  it2 != it1->second.end(); ++it2)
559  {
560  FM_node_type* pFMNode = *it2;
561 
562  node_weight_type tmp_total_weight[2] = {
563  total_weight[0],
564  total_weight[1]
565  };
566  // move out from current partition
567  tmp_total_weight[pFMNode->partition] -= pFMNode->weight;
568  // move into the other partition
569  tmp_total_weight[!pFMNode->partition] += pFMNode->weight;
570 
571  double cur_ratio = (double)total_weight[0]/total_weight[1];
572  double tmp_ratio = (double)tmp_total_weight[0]/tmp_total_weight[1];
573 
574  // the ratio stays in the target range or get closer to the target range
575  if (limbo::abs(tmp_ratio-ratio1)+limbo::abs(tmp_ratio-ratio2)
576  <= limbo::abs(cur_ratio-ratio1)+limbo::abs(cur_ratio-ratio2))
577  {
578  pFMNodeBest = pFMNode;
579  break;
580  }
581  }
582  if (pFMNodeBest) break;
583  }
584  // this is not exit condition yet
585  if (!pFMNodeBest) break;
586 
587 #ifdef DEBUG_FM
588  //gain_bucket.print();
589 #endif
590  // move pFMNodeBest
591  // move out from current partition
592  total_weight[pFMNodeBest->partition] -= pFMNodeBest->weight;
593  // move into the other partition
594  total_weight[!pFMNodeBest->partition] += pFMNodeBest->weight;
595  // remove pFMNodeBest and all its connected nodes from gain bucket
596  gain_bucket.erase(pFMNodeBest);
597  for (typename vector<FM_net_type*>::const_iterator itNet = pFMNodeBest->vNet.begin();
598  itNet != pFMNodeBest->vNet.end(); ++itNet)
599  {
600  for (typename vector<FM_node_type*>::const_iterator itNode = (*itNet)->vNode.begin();
601  itNode != (*itNet)->vNode.end(); ++itNode)
602  {
603  // skip itself and locked nodes
604  if (*itNode == pFMNodeBest || (*itNode)->locked) continue;
605 #ifdef DEBUG_FM
606  //cout << "erasing " << (*itNode)->pNode->tie_id() << endl;
607 #endif
608  gain_bucket.erase(*itNode);
609 #ifdef DEBUG_FM
610  //gain_bucket.print();
611 #endif
612  }
613  }
614 #ifdef DEBUG_FM
615  //gain_bucket.print();
616 #endif
617  // update current cut size
618  for (typename vector<FM_net_type*>::const_iterator itNet = pFMNodeBest->vNet.begin();
619  itNet != pFMNodeBest->vNet.end(); ++itNet)
620  {
621 #if 0
622  for (typename vector<FM_node_type*>::const_iterator itNode = (*itNet)->vNode.begin();
623  itNode != (*itNet)->vNode.end(); ++itNode)
624  {
625  // skip itself
626  if (*itNode == pFMNodeBest) continue;
627  // directly update cut size
628  if (pFMNodeBest->partition == (*itNode)->partition)
629  cur_cutsize += (*itNet)->weight;
630  else
631  cur_cutsize -= (*itNet)->weight;
632  }
633 #else
634  int exclusivePartition = (*itNet)->exclusive_partition(*pFMNodeBest);
635  if (exclusivePartition != 2)
636  {
637  // current node is in the same partition as other nodes in this net
638  if (pFMNodeBest->partition == exclusivePartition)
639  cur_cutsize += (*itNet)->weight;
640  // current node is in different partitions from other nodes in this net
641  else
642  cur_cutsize -= (*itNet)->weight;
643  }
644 #endif
645  }
646  // update partition of pFMNodeBest
647  pFMNodeBest->partition = !pFMNodeBest->partition;
648  pFMNodeBest->locked = true;
649  // insert all its connected nodes
650  for (typename vector<FM_net_type*>::const_iterator itNet = pFMNodeBest->vNet.begin();
651  itNet != pFMNodeBest->vNet.end(); ++itNet)
652  {
653  for (typename vector<FM_node_type*>::const_iterator itNode = (*itNet)->vNode.begin();
654  itNode != (*itNet)->vNode.end(); ++itNode)
655  {
656  if (*itNode == pFMNodeBest || (*itNode)->locked) continue;
657  gain_bucket.insert(*itNode);
658  }
659  }
660 
661  // record current cnt
662  ++cur_cnt;
663 
664 #ifdef DEBUG_FM
665  //gain_bucket.print();
666  assert(limbo::abs(cur_cutsize-this->cutsize()) < 1.0e-6);
667  this->print();
668  cout << "move cnt = " << cur_cnt << ", current cutsize = " << this->cutsize() << endl;
669 #endif
670 
671  if (cur_cutsize < best_cutsize)
672  {
673  best_cutsize = cur_cutsize;
674  best_cnt = cur_cnt;
675  }
676  }
677  // trial mode, recover partition
678  if (target_cnt < 0)
679  {
680  for (typename unordered_map<node_type*, FM_node_type*>::const_iterator it = m_hNode.begin();
681  it != m_hNode.end(); ++it)
682  {
683  FM_node_type* const& pFMNode = it->second;
684  pFMNode->partition = pFMNode->partition_bak;
685  }
686  }
687 
688  return make_pair(best_cutsize, best_cnt);
689  }
690 
691  unordered_map<node_type*, FM_node_type*> m_hNode;
692  vector<FM_net_type*> m_vNet;
694 };
695 
696 } // namespace partition
697 } // namespace algorithms
698 } // namespace limbo
699 
700 #endif
void print_node() const
print node
Definition: FM.h:440
static node_weight_type weight(node_type const &n)
Definition: FM.h:61
net type for the algorithm
Definition: FM.h:142
node_type * pNode
node in the graph
Definition: FM.h:88
int partition
partition id: 0 or 1, -1 for uninitialized
Definition: FM.h:91
net_weight_type gain() const
compute gain
Definition: FM.h:106
Implementation of FM partitioning algorithm.
Definition: FM.h:72
T abs(T const &t)
generalized api can handle both integer and floating points
Definition: Math.h:21
net_weight_type operator()(double ratio1, double ratio2)
Definition: FM.h:392
net_weight_type weight
net weight
Definition: FM.h:145
gain_bucket_type m_gain_bucket
gain buckets
Definition: FM.h:693
virtual void erase(FM_node_type *const &pFMNode)
Definition: FM.h:276
bool operator()(net_weight_type const &g1, net_weight_type const &g2) const
API for comparison.
Definition: FM.h:214
static tie_id_type tie_id(node_type const &n)
Definition: FM.h:57
virtual void insert(FM_node_type *const &pFMNode)
Definition: FM.h:259
node type for the algorithm
Definition: FM.h:86
unordered_map< node_type *, FM_node_type * > m_hNode
FM nodes.
Definition: FM.h:691
pair< net_weight_type, int > single_pass(double ratio1, double ratio2, int target_cnt)
one pass of moving nodes
Definition: FM.h:496
mathematical utilities such as abs
bool add_node(node_type *pNode, int initialPartition)
add node
Definition: FM.h:334
node_weight_type weight
node weight for balancing constraint, like area
Definition: FM.h:93
gain_bucket_type(gain_bucket_type const &rhs)
Definition: FM.h:255
vector< FM_net_type * > vNet
nets related to the node
Definition: FM.h:89
int partition_bak
back up partition for trial pass
Definition: FM.h:92
namespace for Limbo
Definition: GraphUtility.h:22
net_weight_type run(double ratio1, double ratio2)
kernel function to run the algorithm
Definition: FM.h:463
int exclusive_partition(FM_node_type const &fmNode) const
partition excludes a certain node
Definition: FM.h:166
vector< FM_node_type * > vNode
nodes in the net
Definition: FM.h:144
void print() const
print function
Definition: FM.h:397
void print_connection() const
print connection
Definition: FM.h:420
bool operator()(FM_node_type *pFMNode1, FM_node_type *pFMNode2) const
API for comparison.
Definition: FM.h:235
void print() const
print gain bucket
Definition: FM.h:302
vector< FM_net_type * > m_vNet
FM nets.
Definition: FM.h:692
bool add_net(net_weight_type const &weight, Iterator first, Iterator last)
add nets
Definition: FM.h:354
net_weight_type cutsize() const
compute cut size for the net
Definition: FM.h:149
bool operator()(FM_node_type *pFMNode1, FM_node_type *pFMNode2) const
API for comparison.
Definition: FM.h:206
net_weight_type cutsize() const
Definition: FM.h:380