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 \ref 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 large clique, but not necessarily the
52 /// \tparam GR The undirected graph type the algorithm runs on.
54 /// \note %GrossoLocatelliPullanMc provides three different node selection
55 /// rules, from which the most powerful one is used by default.
56 /// For more information, see \ref SelectionRule.
57 template <typename GR>
58 class GrossoLocatelliPullanMc
62 /// \brief Constants for specifying the node selection rule.
64 /// Enum type containing constants for specifying the node selection rule
65 /// for the \ref run() function.
67 /// During the algorithm, nodes are selected for addition to the current
68 /// clique according to the applied rule.
69 /// In general, the PENALTY_BASED rule turned out to be the most powerful
70 /// and the most robust, thus it is the default option.
71 /// However, another selection rule can be specified using the \ref run()
72 /// function with the proper parameter.
75 /// A node is selected randomly without any evaluation at each step.
78 /// A node of maximum degree is selected randomly at each step.
81 /// A node of minimum penalty is selected randomly at each step.
82 /// The node penalties are updated adaptively after each stage of the
89 TEMPLATE_GRAPH_TYPEDEFS(GR);
91 typedef std::vector<int> IntVector;
92 typedef std::vector<char> BoolVector;
93 typedef std::vector<BoolVector> BoolMatrix;
94 // Note: vector<char> is used instead of vector<bool> for efficiency reasons
99 // Internal matrix representation of the graph
103 // The current clique
107 // The best clique found so far
108 BoolVector _best_clique;
111 // The "distances" of the nodes from the current clique.
112 // _delta[u] is the number of nodes in the clique that are
113 // not connected with u.
116 // The current tabu set
119 // Random number generator
124 // Implementation of the RANDOM node selection rule.
125 class RandomSelectionRule
129 // References to the algorithm instance
130 const BoolVector &_clique;
131 const IntVector &_delta;
132 const BoolVector &_tabu;
141 RandomSelectionRule(GrossoLocatelliPullanMc &mc) :
142 _clique(mc._clique), _delta(mc._delta), _tabu(mc._tabu),
143 _rnd(mc._rnd), _n(mc._n)
146 // Return a node index for a feasible add move or -1 if no one exists
147 int nextFeasibleAddNode() const {
148 int start_node = _rnd[_n];
149 for (int i = start_node; i != _n; i++) {
150 if (_delta[i] == 0 && !_tabu[i]) return i;
152 for (int i = 0; i != start_node; i++) {
153 if (_delta[i] == 0 && !_tabu[i]) return i;
158 // Return a node index for a feasible swap move or -1 if no one exists
159 int nextFeasibleSwapNode() const {
160 int start_node = _rnd[_n];
161 for (int i = start_node; i != _n; i++) {
162 if (!_clique[i] && _delta[i] == 1 && !_tabu[i]) return i;
164 for (int i = 0; i != start_node; i++) {
165 if (!_clique[i] && _delta[i] == 1 && !_tabu[i]) return i;
170 // Return a node index for an add move or -1 if no one exists
171 int nextAddNode() const {
172 int start_node = _rnd[_n];
173 for (int i = start_node; i != _n; i++) {
174 if (_delta[i] == 0) return i;
176 for (int i = 0; i != start_node; i++) {
177 if (_delta[i] == 0) return i;
182 // Update internal data structures between stages (if necessary)
185 }; //class RandomSelectionRule
188 // Implementation of the DEGREE_BASED node selection rule.
189 class DegreeBasedSelectionRule
193 // References to the algorithm instance
194 const BoolVector &_clique;
195 const IntVector &_delta;
196 const BoolVector &_tabu;
206 DegreeBasedSelectionRule(GrossoLocatelliPullanMc &mc) :
207 _clique(mc._clique), _delta(mc._delta), _tabu(mc._tabu),
208 _rnd(mc._rnd), _n(mc._n), _deg(_n)
210 for (int i = 0; i != _n; i++) {
212 BoolVector &row = mc._gr[i];
213 for (int j = 0; j != _n; j++) {
220 // Return a node index for a feasible add move or -1 if no one exists
221 int nextFeasibleAddNode() const {
222 int start_node = _rnd[_n];
223 int node = -1, max_deg = -1;
224 for (int i = start_node; i != _n; i++) {
225 if (_delta[i] == 0 && !_tabu[i] && _deg[i] > max_deg) {
230 for (int i = 0; i != start_node; i++) {
231 if (_delta[i] == 0 && !_tabu[i] && _deg[i] > max_deg) {
239 // Return a node index for a feasible swap move or -1 if no one exists
240 int nextFeasibleSwapNode() const {
241 int start_node = _rnd[_n];
242 int node = -1, max_deg = -1;
243 for (int i = start_node; i != _n; i++) {
244 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
250 for (int i = 0; i != start_node; i++) {
251 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
260 // Return a node index for an add move or -1 if no one exists
261 int nextAddNode() const {
262 int start_node = _rnd[_n];
263 int node = -1, max_deg = -1;
264 for (int i = start_node; i != _n; i++) {
265 if (_delta[i] == 0 && _deg[i] > max_deg) {
270 for (int i = 0; i != start_node; i++) {
271 if (_delta[i] == 0 && _deg[i] > max_deg) {
279 // Update internal data structures between stages (if necessary)
282 }; //class DegreeBasedSelectionRule
285 // Implementation of the PENALTY_BASED node selection rule.
286 class PenaltyBasedSelectionRule
290 // References to the algorithm instance
291 const BoolVector &_clique;
292 const IntVector &_delta;
293 const BoolVector &_tabu;
303 PenaltyBasedSelectionRule(GrossoLocatelliPullanMc &mc) :
304 _clique(mc._clique), _delta(mc._delta), _tabu(mc._tabu),
305 _rnd(mc._rnd), _n(mc._n), _penalty(_n, 0)
308 // Return a node index for a feasible add move or -1 if no one exists
309 int nextFeasibleAddNode() const {
310 int start_node = _rnd[_n];
311 int node = -1, min_p = std::numeric_limits<int>::max();
312 for (int i = start_node; i != _n; i++) {
313 if (_delta[i] == 0 && !_tabu[i] && _penalty[i] < min_p) {
318 for (int i = 0; i != start_node; i++) {
319 if (_delta[i] == 0 && !_tabu[i] && _penalty[i] < min_p) {
327 // Return a node index for a feasible swap move or -1 if no one exists
328 int nextFeasibleSwapNode() const {
329 int start_node = _rnd[_n];
330 int node = -1, min_p = std::numeric_limits<int>::max();
331 for (int i = start_node; i != _n; i++) {
332 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
333 _penalty[i] < min_p) {
338 for (int i = 0; i != start_node; i++) {
339 if (!_clique[i] && _delta[i] == 1 && !_tabu[i] &&
340 _penalty[i] < min_p) {
348 // Return a node index for an add move or -1 if no one exists
349 int nextAddNode() const {
350 int start_node = _rnd[_n];
351 int node = -1, min_p = std::numeric_limits<int>::max();
352 for (int i = start_node; i != _n; i++) {
353 if (_delta[i] == 0 && _penalty[i] < min_p) {
358 for (int i = 0; i != start_node; i++) {
359 if (_delta[i] == 0 && _penalty[i] < min_p) {
367 // Update internal data structures between stages (if necessary)
370 }; //class PenaltyBasedSelectionRule
374 /// \brief Constructor.
377 /// The global \ref rnd "random number generator instance" is used
378 /// during the algorithm.
380 /// \param graph The undirected graph the algorithm runs on.
381 GrossoLocatelliPullanMc(const GR& graph) :
382 _graph(graph), _id(_graph), _rnd(rnd)
385 /// \brief Constructor with random seed.
387 /// Constructor with random seed.
389 /// \param graph The undirected graph the algorithm runs on.
390 /// \param seed Seed value for the internal random number generator
391 /// that is used during the algorithm.
392 GrossoLocatelliPullanMc(const GR& graph, int seed) :
393 _graph(graph), _id(_graph), _rnd(seed)
396 /// \brief Constructor with random number generator.
398 /// Constructor with random number generator.
400 /// \param graph The undirected graph the algorithm runs on.
401 /// \param random A random number generator that is used during the
403 GrossoLocatelliPullanMc(const GR& graph, const Random& random) :
404 _graph(graph), _id(_graph), _rnd(random)
407 /// \name Execution Control
410 /// \brief Runs the algorithm.
412 /// This function runs the algorithm.
414 /// \param step_num The maximum number of node selections (steps)
415 /// during the search process.
416 /// This parameter controls the running time and the success of the
417 /// algorithm. For larger values, the algorithm runs slower but it more
418 /// likely finds larger cliques. For smaller values, the algorithm is
419 /// faster but probably gives worse results.
420 /// \param rule The node selection rule. For more information, see
421 /// \ref SelectionRule.
423 /// \return The size of the found clique.
424 int run(int step_num = 100000,
425 SelectionRule rule = PENALTY_BASED)
430 return start<RandomSelectionRule>(step_num);
432 return start<DegreeBasedSelectionRule>(step_num);
434 return start<PenaltyBasedSelectionRule>(step_num);
436 return 0; // avoid warning
441 /// \name Query Functions
444 /// \brief The size of the found clique
446 /// This function returns the size of the found clique.
448 /// \pre run() must be called before using this function.
449 int cliqueSize() const {
453 /// \brief Gives back the found clique in a \c bool node map
455 /// This function gives back the characteristic vector of the found
456 /// clique in the given node map.
457 /// It must be a \ref concepts::WriteMap "writable" node map with
458 /// \c bool (or convertible) value type.
460 /// \pre run() must be called before using this function.
461 template <typename CliqueMap>
462 void cliqueMap(CliqueMap &map) const {
463 for (NodeIt n(_graph); n != INVALID; ++n) {
464 map[n] = static_cast<bool>(_best_clique[_id[n]]);
468 /// \brief Iterator to list the nodes of the found clique
470 /// This iterator class lists the nodes of the found clique.
471 /// Before using it, you must allocate a GrossoLocatelliPullanMc instance
472 /// and call its \ref GrossoLocatelliPullanMc::run() "run()" method.
474 /// The following example prints out the IDs of the nodes in the found
477 /// GrossoLocatelliPullanMc<Graph> mc(g);
479 /// for (GrossoLocatelliPullanMc<Graph>::CliqueNodeIt n(mc);
480 /// n != INVALID; ++n)
482 /// std::cout << g.id(n) << std::endl;
496 /// \param mc The algorithm instance.
497 CliqueNodeIt(const GrossoLocatelliPullanMc &mc)
501 for (_it = NodeIt(mc._graph); _it != INVALID && !_map[_it]; ++_it) ;
504 /// Conversion to \c Node
505 operator Node() const { return _it; }
507 bool operator==(Invalid) const { return _it == INVALID; }
508 bool operator!=(Invalid) const { return _it != INVALID; }
511 CliqueNodeIt &operator++() {
512 for (++_it; _it != INVALID && !_map[_it]; ++_it) ;
516 /// Postfix incrementation
518 /// Postfix incrementation.
520 /// \warning This incrementation returns a \c Node, not a
521 /// \c CliqueNodeIt as one may expect.
522 typename GR::Node operator++(int) {
534 // Adds a node to the current clique
535 void addCliqueNode(int u) {
536 if (_clique[u]) return;
539 BoolVector &row = _gr[u];
540 for (int i = 0; i != _n; i++) {
541 if (!row[i]) _delta[i]++;
545 // Removes a node from the current clique
546 void delCliqueNode(int u) {
547 if (!_clique[u]) return;
550 BoolVector &row = _gr[u];
551 for (int i = 0; i != _n; i++) {
552 if (!row[i]) _delta[i]--;
556 // Initialize data structures
558 _n = countNodes(_graph);
560 for (NodeIt u(_graph); u != INVALID; ++u) {
564 _gr.resize(_n, BoolVector(_n, false));
566 for (NodeIt u(_graph); u != INVALID; ++u) {
567 for (IncEdgeIt e(_graph, u); e != INVALID; ++e) {
568 int vi = _id[_graph.runningNode(e)];
576 _clique.resize(_n, false);
578 _best_clique.clear();
579 _best_clique.resize(_n, false);
582 _delta.resize(_n, 0);
584 _tabu.resize(_n, false);
587 // Executes the algorithm
588 template <typename SelectionRuleImpl>
589 int start(int max_select) {
590 // Options for the restart rule
591 const bool delta_based_restart = true;
592 const int restart_delta_limit = 4;
594 if (_n == 0) return 0;
596 _best_clique[0] = true;
601 // Iterated local search
602 SelectionRuleImpl sel_method(*this);
604 IntVector restart_nodes;
606 while (select < max_select) {
608 // Perturbation/restart
609 if (delta_based_restart) {
610 restart_nodes.clear();
611 for (int i = 0; i != _n; i++) {
612 if (_delta[i] >= restart_delta_limit)
613 restart_nodes.push_back(i);
617 if (restart_nodes.size() > 0) {
618 rs_node = restart_nodes[_rnd[restart_nodes.size()]];
622 BoolVector &row = _gr[rs_node];
623 for (int i = 0; i != _n; i++) {
624 if (_clique[i] && !row[i]) delCliqueNode(i);
626 addCliqueNode(rs_node);
630 _tabu.resize(_n, false);
631 bool tabu_empty = true;
632 int max_swap = _size;
633 while (select < max_select) {
636 if ((u = sel_method.nextFeasibleAddNode()) != -1) {
639 if (tabu_empty) max_swap = _size;
641 else if ((u = sel_method.nextFeasibleSwapNode()) != -1) {
642 // Feasible swap move
644 BoolVector &row = _gr[u];
645 for (int i = 0; i != _n; i++) {
646 if (_clique[i] && !row[i]) {
655 if (--max_swap <= 0) break;
657 else if ((u = sel_method.nextAddNode()) != -1) {
658 // Non-feasible add move
663 if (_size > _best_size) {
664 _best_clique = _clique;
666 if (_best_size == _n) return _best_size;
674 }; //class GrossoLocatelliPullanMc
680 #endif //LEMON_GROSSO_LOCATELLI_PULLAN_MC_H