1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
3 * This file is a part of LEMON, a generic C++ optimization library.
5 * Copyright (C) 2003-2010
6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
9 * Permission to use, modify and distribute this software is granted
10 * provided that this copyright notice appears in all copies. For
11 * precise terms see the accompanying LICENSE file.
13 * This software is provided "AS IS" with no warranty of any kind,
14 * express or implied, and with no claim as to its suitability for any
19 #ifndef LEMON_GROSSO_LOCATELLI_PULLAN_MC_H
20 #define LEMON_GROSSO_LOCATELLI_PULLAN_MC_H
22 /// \ingroup approx_algs
25 /// \brief The iterated local search algorithm of Grosso, Locatelli, and Pullan
26 /// for the maximum clique problem
30 #include <lemon/core.h>
31 #include <lemon/random.h>
35 /// \addtogroup approx_algs
38 /// \brief Implementation of the iterated local search algorithm of Grosso,
39 /// Locatelli, and Pullan for the maximum clique problem
41 /// \ref GrossoLocatelliPullanMc implements the iterated local search
42 /// algorithm of Grosso, Locatelli, and Pullan for solving the \e maximum
43 /// \e clique \e problem \cite grosso08maxclique.
44 /// It is to find the largest complete subgraph (\e clique) in an
45 /// undirected graph, i.e., the largest set of nodes where each
46 /// pair of nodes is connected.
48 /// This class provides a simple but highly efficient and robust heuristic
49 /// method that quickly finds a quite large clique, but not necessarily the
51 /// The algorithm performs a certain number of iterations to find several
52 /// cliques and selects the largest one among them. Various limits can be
53 /// specified to control the running time and the effectiveness of the
56 /// \tparam GR The undirected graph type the algorithm runs on.
58 /// \note %GrossoLocatelliPullanMc provides three different node selection
59 /// rules, from which the most powerful one is used by default.
60 /// For more information, see \ref SelectionRule.
61 template <typename GR>
62 class GrossoLocatelliPullanMc
66 /// \brief Constants for specifying the node selection rule.
68 /// Enum type containing constants for specifying the node selection rule
69 /// for the \ref run() function.
71 /// During the algorithm, nodes are selected for addition to the current
72 /// clique according to the applied rule.
73 /// In general, the PENALTY_BASED rule turned out to be the most powerful
74 /// and the most robust, thus it is the default option.
75 /// However, another selection rule can be specified using the \ref run()
76 /// function with the proper parameter.
79 /// A node is selected randomly without any evaluation at each step.
82 /// A node of maximum degree is selected randomly at each step.
85 /// A node of minimum penalty is selected randomly at each step.
86 /// The node penalties are updated adaptively after each stage of the
91 /// \brief Constants for the causes of search termination.
93 /// Enum type containing constants for the different causes of search
94 /// termination. The \ref run() function returns one of these values.
95 enum TerminationCause {
97 /// The iteration count limit is reached.
100 /// The step count limit is reached.
103 /// The clique size limit is reached.
109 TEMPLATE_GRAPH_TYPEDEFS(GR);
111 typedef std::vector<int> IntVector;
112 typedef std::vector<char> BoolVector;
113 typedef std::vector<BoolVector> BoolMatrix;
114 // Note: vector<char> is used instead of vector<bool> for efficiency reasons
116 // The underlying graph
120 // Internal matrix representation of the graph
125 bool _delta_based_restart;
126 int _restart_delta_limit;
129 int _iteration_limit;
133 // The current clique
137 // The best clique found so far
138 BoolVector _best_clique;
141 // The "distances" of the nodes from the current clique.
142 // _delta[u] is the number of nodes in the clique that are
143 // not connected with u.
146 // The current tabu set
149 // Random number generator
154 // Implementation of the RANDOM node selection rule.
155 class RandomSelectionRule
159 // References to the algorithm instance
160 const BoolVector &_clique;
161 const IntVector &_delta;
162 const BoolVector &_tabu;
171 RandomSelectionRule(GrossoLocatelliPullanMc &mc) :
172 _clique(mc._clique), _delta(mc._delta), _tabu(mc._tabu),
173 _rnd(mc._rnd), _n(mc._n)
176 // Return a node index for a feasible add move or -1 if no one exists
177 int nextFeasibleAddNode() const {
178 int start_node = _rnd[_n];
179 for (int i = start_node; i != _n; i++) {
180 if (_delta[i] == 0 && !_tabu[i]) return i;
182 for (int i = 0; i != start_node; i++) {
183 if (_delta[i] == 0 && !_tabu[i]) return i;
188 // Return a node index for a feasible swap move or -1 if no one exists
189 int nextFeasibleSwapNode() const {
190 int start_node = _rnd[_n];
191 for (int i = start_node; i != _n; i++) {
192 if (!_clique[i] && _delta[i] == 1 && !_tabu[i]) return i;
194 for (int i = 0; i != start_node; i++) {
195 if (!_clique[i] && _delta[i] == 1 && !_tabu[i]) return i;
200 // Return a node index for an add move or -1 if no one exists
201 int nextAddNode() const {
202 int start_node = _rnd[_n];
203 for (int i = start_node; i != _n; i++) {
204 if (_delta[i] == 0) return i;
206 for (int i = 0; i != start_node; i++) {
207 if (_delta[i] == 0) return i;
212 // Update internal data structures between stages (if necessary)
215 }; //class RandomSelectionRule
218 // Implementation of the DEGREE_BASED node selection rule.
219 class DegreeBasedSelectionRule
223 // References to the algorithm instance
224 const BoolVector &_clique;
225 const IntVector &_delta;
226 const BoolVector &_tabu;
236 DegreeBasedSelectionRule(GrossoLocatelliPullanMc &mc) :
237 _clique(mc._clique), _delta(mc._delta), _tabu(mc._tabu),
238 _rnd(mc._rnd), _n(mc._n), _deg(_n)
240 for (int i = 0; i != _n; i++) {
242 BoolVector &row = mc._gr[i];
243 for (int j = 0; j != _n; j++) {
250 // Return a node index for a feasible add move or -1 if no one exists
251 int nextFeasibleAddNode() const {
252 int start_node = _rnd[_n];
253 int node = -1, max_deg = -1;
254 for (int i = start_node; i != _n; i++) {
255 if (_delta[i] == 0 && !_tabu[i] && _deg[i] > max_deg) {
260 for (int i = 0; i != start_node; i++) {
261 if (_delta[i] == 0 && !_tabu[i] && _deg[i] > max_deg) {
269 // Return a node index for a feasible swap move or -1 if no one exists
270 int nextFeasibleSwapNode() const {
271 int start_node = _rnd[_n];
272 int node = -1, max_deg = -1;
273 for (int i = start_node; i != _n; i++) {
274 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
280 for (int i = 0; i != start_node; i++) {
281 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
290 // Return a node index for an add move or -1 if no one exists
291 int nextAddNode() const {
292 int start_node = _rnd[_n];
293 int node = -1, max_deg = -1;
294 for (int i = start_node; i != _n; i++) {
295 if (_delta[i] == 0 && _deg[i] > max_deg) {
300 for (int i = 0; i != start_node; i++) {
301 if (_delta[i] == 0 && _deg[i] > max_deg) {
309 // Update internal data structures between stages (if necessary)
312 }; //class DegreeBasedSelectionRule
315 // Implementation of the PENALTY_BASED node selection rule.
316 class PenaltyBasedSelectionRule
320 // References to the algorithm instance
321 const BoolVector &_clique;
322 const IntVector &_delta;
323 const BoolVector &_tabu;
333 PenaltyBasedSelectionRule(GrossoLocatelliPullanMc &mc) :
334 _clique(mc._clique), _delta(mc._delta), _tabu(mc._tabu),
335 _rnd(mc._rnd), _n(mc._n), _penalty(_n, 0)
338 // Return a node index for a feasible add move or -1 if no one exists
339 int nextFeasibleAddNode() const {
340 int start_node = _rnd[_n];
341 int node = -1, min_p = std::numeric_limits<int>::max();
342 for (int i = start_node; i != _n; i++) {
343 if (_delta[i] == 0 && !_tabu[i] && _penalty[i] < min_p) {
348 for (int i = 0; i != start_node; i++) {
349 if (_delta[i] == 0 && !_tabu[i] && _penalty[i] < min_p) {
357 // Return a node index for a feasible swap move or -1 if no one exists
358 int nextFeasibleSwapNode() const {
359 int start_node = _rnd[_n];
360 int node = -1, min_p = std::numeric_limits<int>::max();
361 for (int i = start_node; i != _n; i++) {
362 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
363 _penalty[i] < min_p) {
368 for (int i = 0; i != start_node; i++) {
369 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
370 _penalty[i] < min_p) {
378 // Return a node index for an add move or -1 if no one exists
379 int nextAddNode() const {
380 int start_node = _rnd[_n];
381 int node = -1, min_p = std::numeric_limits<int>::max();
382 for (int i = start_node; i != _n; i++) {
383 if (_delta[i] == 0 && _penalty[i] < min_p) {
388 for (int i = 0; i != start_node; i++) {
389 if (_delta[i] == 0 && _penalty[i] < min_p) {
397 // Update internal data structures between stages (if necessary)
400 }; //class PenaltyBasedSelectionRule
404 /// \brief Constructor.
407 /// The global \ref rnd "random number generator instance" is used
408 /// during the algorithm.
410 /// \param graph The undirected graph the algorithm runs on.
411 GrossoLocatelliPullanMc(const GR& graph) :
412 _graph(graph), _id(_graph), _rnd(rnd)
417 /// \brief Constructor with random seed.
419 /// Constructor with random seed.
421 /// \param graph The undirected graph the algorithm runs on.
422 /// \param seed Seed value for the internal random number generator
423 /// that is used during the algorithm.
424 GrossoLocatelliPullanMc(const GR& graph, int seed) :
425 _graph(graph), _id(_graph), _rnd(seed)
430 /// \brief Constructor with random number generator.
432 /// Constructor with random number generator.
434 /// \param graph The undirected graph the algorithm runs on.
435 /// \param random A random number generator that is used during the
437 GrossoLocatelliPullanMc(const GR& graph, const Random& random) :
438 _graph(graph), _id(_graph), _rnd(random)
443 /// \name Execution Control
444 /// The \ref run() function can be used to execute the algorithm.\n
445 /// The functions \ref iterationLimit(int), \ref stepLimit(int), and
446 /// \ref sizeLimit(int) can be used to specify various limits for the
451 /// \brief Sets the maximum number of iterations.
453 /// This function sets the maximum number of iterations.
454 /// Each iteration of the algorithm finds a maximal clique (but not
455 /// necessarily the largest one) by performing several search steps
456 /// (node selections).
458 /// This limit controls the running time and the success of the
459 /// algorithm. For larger values, the algorithm runs slower, but it more
460 /// likely finds larger cliques. For smaller values, the algorithm is
461 /// faster but probably gives worse results.
463 /// The default value is \c 1000.
464 /// \c -1 means that number of iterations is not limited.
466 /// \warning You should specify a reasonable limit for the number of
467 /// iterations and/or the number of search steps.
469 /// \return <tt>(*this)</tt>
471 /// \sa stepLimit(int)
472 /// \sa sizeLimit(int)
473 GrossoLocatelliPullanMc& iterationLimit(int limit) {
474 _iteration_limit = limit;
478 /// \brief Sets the maximum number of search steps.
480 /// This function sets the maximum number of elementary search steps.
481 /// Each iteration of the algorithm finds a maximal clique (but not
482 /// necessarily the largest one) by performing several search steps
483 /// (node selections).
485 /// This limit controls the running time and the success of the
486 /// algorithm. For larger values, the algorithm runs slower, but it more
487 /// likely finds larger cliques. For smaller values, the algorithm is
488 /// faster but probably gives worse results.
490 /// The default value is \c -1, which means that number of steps
491 /// is not limited explicitly. However, the number of iterations is
492 /// limited and each iteration performs a finite number of search steps.
494 /// \warning You should specify a reasonable limit for the number of
495 /// iterations and/or the number of search steps.
497 /// \return <tt>(*this)</tt>
499 /// \sa iterationLimit(int)
500 /// \sa sizeLimit(int)
501 GrossoLocatelliPullanMc& stepLimit(int limit) {
506 /// \brief Sets the desired clique size.
508 /// This function sets the desired clique size that serves as a search
509 /// limit. If a clique of this size (or a larger one) is found, then the
510 /// algorithm terminates.
512 /// This function is especially useful if you know an exact upper bound
513 /// for the size of the cliques in the graph or if any clique above
514 /// a certain size limit is sufficient for your application.
516 /// The default value is \c -1, which means that the size limit is set to
517 /// the number of nodes in the graph.
519 /// \return <tt>(*this)</tt>
521 /// \sa iterationLimit(int)
522 /// \sa stepLimit(int)
523 GrossoLocatelliPullanMc& sizeLimit(int limit) {
528 /// \brief The maximum number of iterations.
530 /// This function gives back the maximum number of iterations.
531 /// \c -1 means that no limit is specified.
533 /// \sa iterationLimit(int)
534 int iterationLimit() const {
535 return _iteration_limit;
538 /// \brief The maximum number of search steps.
540 /// This function gives back the maximum number of search steps.
541 /// \c -1 means that no limit is specified.
543 /// \sa stepLimit(int)
544 int stepLimit() const {
548 /// \brief The desired clique size.
550 /// This function gives back the desired clique size that serves as a
551 /// search limit. \c -1 means that this limit is set to the number of
552 /// nodes in the graph.
554 /// \sa sizeLimit(int)
555 int sizeLimit() const {
559 /// \brief Runs the algorithm.
561 /// This function runs the algorithm. If one of the specified limits
562 /// is reached, the search process terminates.
564 /// \param rule The node selection rule. For more information, see
565 /// \ref SelectionRule.
567 /// \return The termination cause of the search. For more information,
568 /// see \ref TerminationCause.
569 TerminationCause run(SelectionRule rule = PENALTY_BASED)
574 return start<RandomSelectionRule>();
576 return start<DegreeBasedSelectionRule>();
578 return start<PenaltyBasedSelectionRule>();
584 /// \name Query Functions
585 /// The results of the algorithm can be obtained using these functions.\n
586 /// The run() function must be called before using them.
590 /// \brief The size of the found clique
592 /// This function returns the size of the found clique.
594 /// \pre run() must be called before using this function.
595 int cliqueSize() const {
599 /// \brief Gives back the found clique in a \c bool node map
601 /// This function gives back the characteristic vector of the found
602 /// clique in the given node map.
603 /// It must be a \ref concepts::WriteMap "writable" node map with
604 /// \c bool (or convertible) value type.
606 /// \pre run() must be called before using this function.
607 template <typename CliqueMap>
608 void cliqueMap(CliqueMap &map) const {
609 for (NodeIt n(_graph); n != INVALID; ++n) {
610 map[n] = static_cast<bool>(_best_clique[_id[n]]);
614 /// \brief Iterator to list the nodes of the found clique
616 /// This iterator class lists the nodes of the found clique.
617 /// Before using it, you must allocate a GrossoLocatelliPullanMc instance
618 /// and call its \ref GrossoLocatelliPullanMc::run() "run()" method.
620 /// The following example prints out the IDs of the nodes in the found
623 /// GrossoLocatelliPullanMc<Graph> mc(g);
625 /// for (GrossoLocatelliPullanMc<Graph>::CliqueNodeIt n(mc);
626 /// n != INVALID; ++n)
628 /// std::cout << g.id(n) << std::endl;
642 /// \param mc The algorithm instance.
643 CliqueNodeIt(const GrossoLocatelliPullanMc &mc)
647 for (_it = NodeIt(mc._graph); _it != INVALID && !_map[_it]; ++_it) ;
650 /// Conversion to \c Node
651 operator Node() const { return _it; }
653 bool operator==(Invalid) const { return _it == INVALID; }
654 bool operator!=(Invalid) const { return _it != INVALID; }
657 CliqueNodeIt &operator++() {
658 for (++_it; _it != INVALID && !_map[_it]; ++_it) ;
662 /// Postfix incrementation
664 /// Postfix incrementation.
666 /// \warning This incrementation returns a \c Node, not a
667 /// \c CliqueNodeIt as one may expect.
668 typename GR::Node operator++(int) {
680 // Initialize search options and limits
683 _delta_based_restart = true;
684 _restart_delta_limit = 4;
687 _iteration_limit = 1000;
688 _step_limit = -1; // this is disabled by default
689 _size_limit = -1; // this is disabled by default
692 // Adds a node to the current clique
693 void addCliqueNode(int u) {
694 if (_clique[u]) return;
697 BoolVector &row = _gr[u];
698 for (int i = 0; i != _n; i++) {
699 if (!row[i]) _delta[i]++;
703 // Removes a node from the current clique
704 void delCliqueNode(int u) {
705 if (!_clique[u]) return;
708 BoolVector &row = _gr[u];
709 for (int i = 0; i != _n; i++) {
710 if (!row[i]) _delta[i]--;
714 // Initialize data structures
716 _n = countNodes(_graph);
718 for (NodeIt u(_graph); u != INVALID; ++u) {
722 _gr.resize(_n, BoolVector(_n, false));
724 for (NodeIt u(_graph); u != INVALID; ++u) {
725 for (IncEdgeIt e(_graph, u); e != INVALID; ++e) {
726 int vi = _id[_graph.runningNode(e)];
734 _clique.resize(_n, false);
736 _best_clique.clear();
737 _best_clique.resize(_n, false);
740 _delta.resize(_n, 0);
742 _tabu.resize(_n, false);
745 // Executes the algorithm
746 template <typename SelectionRuleImpl>
747 TerminationCause start() {
748 if (_n == 0) return SIZE_LIMIT;
750 _best_clique[0] = true;
755 // Iterated local search algorithm
756 const int max_size = _size_limit >= 0 ? _size_limit : _n;
757 const int max_restart = _iteration_limit >= 0 ?
758 _iteration_limit : std::numeric_limits<int>::max();
759 const int max_select = _step_limit >= 0 ?
760 _step_limit : std::numeric_limits<int>::max();
762 SelectionRuleImpl sel_method(*this);
763 int select = 0, restart = 0;
764 IntVector restart_nodes;
765 while (select < max_select && restart < max_restart) {
767 // Perturbation/restart
769 if (_delta_based_restart) {
770 restart_nodes.clear();
771 for (int i = 0; i != _n; i++) {
772 if (_delta[i] >= _restart_delta_limit)
773 restart_nodes.push_back(i);
777 if (restart_nodes.size() > 0) {
778 rs_node = restart_nodes[_rnd[restart_nodes.size()]];
782 BoolVector &row = _gr[rs_node];
783 for (int i = 0; i != _n; i++) {
784 if (_clique[i] && !row[i]) delCliqueNode(i);
786 addCliqueNode(rs_node);
790 _tabu.resize(_n, false);
791 bool tabu_empty = true;
792 int max_swap = _size;
793 while (select < max_select) {
796 if ((u = sel_method.nextFeasibleAddNode()) != -1) {
799 if (tabu_empty) max_swap = _size;
801 else if ((u = sel_method.nextFeasibleSwapNode()) != -1) {
802 // Feasible swap move
804 BoolVector &row = _gr[u];
805 for (int i = 0; i != _n; i++) {
806 if (_clique[i] && !row[i]) {
815 if (--max_swap <= 0) break;
817 else if ((u = sel_method.nextAddNode()) != -1) {
818 // Non-feasible add move
823 if (_size > _best_size) {
824 _best_clique = _clique;
826 if (_best_size >= max_size) return SIZE_LIMIT;
831 return (restart >= max_restart ? ITERATION_LIMIT : STEP_LIMIT);
834 }; //class GrossoLocatelliPullanMc
840 #endif //LEMON_GROSSO_LOCATELLI_PULLAN_MC_H