COIN-OR::LEMON - Graph Library

Ticket #179: 179-9-add-ho-00dd2f3ccfa9.patch

File 179-9-add-ho-00dd2f3ccfa9.patch, 21.2 KB (added by Peter Kovacs, 15 years ago)
  • lemon/Makefile.am

    # HG changeset patch
    # User Peter Kovacs <kpeter@inf.elte.hu>
    # Date 1250020419 -7200
    # Node ID 00dd2f3ccfa9844f9a7c98b86948f9e56462a06b
    # Parent  f7ddbf131179186bdcd76016762a1fc82c6b5a45
    Add HartmannOrlin algorithm class (#179)
    This algorithm is an improved version of Karp's original method,
    it applies an efficient early termination scheme.
    The interface is the same as Karp's and Howard's interface.
    
    diff --git a/lemon/Makefile.am b/lemon/Makefile.am
    a b  
    8383        lemon/gomory_hu.h \
    8484        lemon/graph_to_eps.h \
    8585        lemon/grid_graph.h \
     86        lemon/hartmann_orlin.h \
    8687        lemon/howard.h \
    8788        lemon/hypercube_graph.h \
    8889        lemon/karp.h \
  • new file lemon/hartmann_orlin.h

    diff --git a/lemon/hartmann_orlin.h b/lemon/hartmann_orlin.h
    new file mode 100644
    - +  
     1/* -*- C++ -*-
     2 *
     3 * This file is a part of LEMON, a generic C++ optimization library
     4 *
     5 * Copyright (C) 2003-2008
     6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7 * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8 *
     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.
     12 *
     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
     15 * purpose.
     16 *
     17 */
     18
     19#ifndef LEMON_HARTMANN_ORLIN_H
     20#define LEMON_HARTMANN_ORLIN_H
     21
     22/// \ingroup shortest_path
     23///
     24/// \file
     25/// \brief Hartmann-Orlin's algorithm for finding a minimum mean cycle.
     26
     27#include <vector>
     28#include <lemon/core.h>
     29#include <lemon/path.h>
     30#include <lemon/tolerance.h>
     31#include <lemon/connectivity.h>
     32
     33namespace lemon {
     34
     35  /// \brief Default traits class of HartmannOrlin algorithm.
     36  ///
     37  /// Default traits class of HartmannOrlin algorithm.
     38  /// \tparam GR The type of the digraph.
     39  /// \tparam LEN The type of the length map.
     40  /// It must conform to the \ref concepts::Rea_data "Rea_data" concept.
     41#ifdef DOXYGEN
     42  template <typename GR, typename LEN>
     43#else
     44  template <typename GR, typename LEN,
     45    bool integer = std::numeric_limits<typename LEN::Value>::is_integer>
     46#endif
     47  struct HartmannOrlinDefaultTraits
     48  {
     49    /// The type of the digraph
     50    typedef GR Digraph;
     51    /// The type of the length map
     52    typedef LEN LengthMap;
     53    /// The type of the arc lengths
     54    typedef typename LengthMap::Value Value;
     55
     56    /// \brief The large value type used for internal computations
     57    ///
     58    /// The large value type used for internal computations.
     59    /// It is \c long \c long if the \c Value type is integer,
     60    /// otherwise it is \c double.
     61    /// \c Value must be convertible to \c LargeValue.
     62    typedef double LargeValue;
     63
     64    /// The tolerance type used for internal computations
     65    typedef lemon::Tolerance<LargeValue> Tolerance;
     66
     67    /// \brief The path type of the found cycles
     68    ///
     69    /// The path type of the found cycles.
     70    /// It must conform to the \ref lemon::concepts::Path "Path" concept
     71    /// and it must have an \c addBack() function.
     72    typedef lemon::Path<Digraph> Path;
     73  };
     74
     75  // Default traits class for integer value types
     76  template <typename GR, typename LEN>
     77  struct HartmannOrlinDefaultTraits<GR, LEN, true>
     78  {
     79    typedef GR Digraph;
     80    typedef LEN LengthMap;
     81    typedef typename LengthMap::Value Value;
     82#ifdef LEMON_HAVE_LONG_LONG
     83    typedef long long LargeValue;
     84#else
     85    typedef long LargeValue;
     86#endif
     87    typedef lemon::Tolerance<LargeValue> Tolerance;
     88    typedef lemon::Path<Digraph> Path;
     89  };
     90
     91
     92  /// \addtogroup shortest_path
     93  /// @{
     94
     95  /// \brief Implementation of the Hartmann-Orlin algorithm for finding
     96  /// a minimum mean cycle.
     97  ///
     98  /// This class implements the Hartmann-Orlin algorithm for finding
     99  /// a directed cycle of minimum mean length (cost) in a digraph.
     100  /// It is an improved version of \ref Karp "Karp's original algorithm",
     101  /// it applies an efficient early termination scheme.
     102  ///
     103  /// \tparam GR The type of the digraph the algorithm runs on.
     104  /// \tparam LEN The type of the length map. The default
     105  /// map type is \ref concepts::Digraph::ArcMap "GR::ArcMap<int>".
     106#ifdef DOXYGEN
     107  template <typename GR, typename LEN, typename TR>
     108#else
     109  template < typename GR,
     110             typename LEN = typename GR::template ArcMap<int>,
     111             typename TR = HartmannOrlinDefaultTraits<GR, LEN> >
     112#endif
     113  class HartmannOrlin
     114  {
     115  public:
     116
     117    /// The type of the digraph
     118    typedef typename TR::Digraph Digraph;
     119    /// The type of the length map
     120    typedef typename TR::LengthMap LengthMap;
     121    /// The type of the arc lengths
     122    typedef typename TR::Value Value;
     123
     124    /// \brief The large value type
     125    ///
     126    /// The large value type used for internal computations.
     127    /// Using the \ref HartmannOrlinDefaultTraits "default traits class",
     128    /// it is \c long \c long if the \c Value type is integer,
     129    /// otherwise it is \c double.
     130    typedef typename TR::LargeValue LargeValue;
     131
     132    /// The tolerance type
     133    typedef typename TR::Tolerance Tolerance;
     134
     135    /// \brief The path type of the found cycles
     136    ///
     137    /// The path type of the found cycles.
     138    /// Using the \ref HartmannOrlinDefaultTraits "default traits class",
     139    /// it is \ref lemon::Path "Path<Digraph>".
     140    typedef typename TR::Path Path;
     141
     142    /// The \ref HartmannOrlinDefaultTraits "traits class" of the algorithm
     143    typedef TR Traits;
     144
     145  private:
     146
     147    TEMPLATE_DIGRAPH_TYPEDEFS(Digraph);
     148
     149    // Data sturcture for path data
     150    struct PathData
     151    {
     152      bool found;
     153      LargeValue dist;
     154      Arc pred;
     155      PathData(bool f = false, LargeValue d = 0, Arc p = INVALID) :
     156        found(f), dist(d), pred(p) {}
     157    };
     158
     159    typedef typename Digraph::template NodeMap<std::vector<PathData> >
     160      PathDataNodeMap;
     161
     162  private:
     163
     164    // The digraph the algorithm runs on
     165    const Digraph &_gr;
     166    // The length of the arcs
     167    const LengthMap &_length;
     168
     169    // Data for storing the strongly connected components
     170    int _comp_num;
     171    typename Digraph::template NodeMap<int> _comp;
     172    std::vector<std::vector<Node> > _comp_nodes;
     173    std::vector<Node>* _nodes;
     174    typename Digraph::template NodeMap<std::vector<Arc> > _out_arcs;
     175
     176    // Data for the found cycles
     177    bool _curr_found, _best_found;
     178    LargeValue _curr_length, _best_length;
     179    int _curr_size, _best_size;
     180    Node _curr_node, _best_node;
     181    int _curr_level, _best_level;
     182
     183    Path *_cycle_path;
     184    bool _local_path;
     185
     186    // Node map for storing path data
     187    PathDataNodeMap _data;
     188    // The processed nodes in the last round
     189    std::vector<Node> _process;
     190
     191    Tolerance _tolerance;
     192
     193  public:
     194
     195    /// \name Named Template Parameters
     196    /// @{
     197
     198    template <typename T>
     199    struct SetLargeValueTraits : public Traits {
     200      typedef T LargeValue;
     201      typedef lemon::Tolerance<T> Tolerance;
     202    };
     203
     204    /// \brief \ref named-templ-param "Named parameter" for setting
     205    /// \c LargeValue type.
     206    ///
     207    /// \ref named-templ-param "Named parameter" for setting \c LargeValue
     208    /// type. It is used for internal computations in the algorithm.
     209    template <typename T>
     210    struct SetLargeValue
     211      : public HartmannOrlin<GR, LEN, SetLargeValueTraits<T> > {
     212      typedef HartmannOrlin<GR, LEN, SetLargeValueTraits<T> > Create;
     213    };
     214
     215    template <typename T>
     216    struct SetPathTraits : public Traits {
     217      typedef T Path;
     218    };
     219
     220    /// \brief \ref named-templ-param "Named parameter" for setting
     221    /// \c %Path type.
     222    ///
     223    /// \ref named-templ-param "Named parameter" for setting the \c %Path
     224    /// type of the found cycles.
     225    /// It must conform to the \ref lemon::concepts::Path "Path" concept
     226    /// and it must have an \c addFront() function.
     227    template <typename T>
     228    struct SetPath
     229      : public HartmannOrlin<GR, LEN, SetPathTraits<T> > {
     230      typedef HartmannOrlin<GR, LEN, SetPathTraits<T> > Create;
     231    };
     232
     233    /// @}
     234
     235  public:
     236
     237    /// \brief Constructor.
     238    ///
     239    /// The constructor of the class.
     240    ///
     241    /// \param digraph The digraph the algorithm runs on.
     242    /// \param length The lengths (costs) of the arcs.
     243    HartmannOrlin( const Digraph &digraph,
     244                   const LengthMap &length ) :
     245      _gr(digraph), _length(length), _comp(digraph), _out_arcs(digraph),
     246      _best_found(false), _best_length(0), _best_size(1),
     247      _cycle_path(NULL), _local_path(false), _data(digraph)
     248    {}
     249
     250    /// Destructor.
     251    ~HartmannOrlin() {
     252      if (_local_path) delete _cycle_path;
     253    }
     254
     255    /// \brief Set the path structure for storing the found cycle.
     256    ///
     257    /// This function sets an external path structure for storing the
     258    /// found cycle.
     259    ///
     260    /// If you don't call this function before calling \ref run() or
     261    /// \ref findMinMean(), it will allocate a local \ref Path "path"
     262    /// structure. The destuctor deallocates this automatically
     263    /// allocated object, of course.
     264    ///
     265    /// \note The algorithm calls only the \ref lemon::Path::addFront()
     266    /// "addFront()" function of the given path structure.
     267    ///
     268    /// \return <tt>(*this)</tt>
     269    HartmannOrlin& cycle(Path &path) {
     270      if (_local_path) {
     271        delete _cycle_path;
     272        _local_path = false;
     273      }
     274      _cycle_path = &path;
     275      return *this;
     276    }
     277
     278    /// \name Execution control
     279    /// The simplest way to execute the algorithm is to call the \ref run()
     280    /// function.\n
     281    /// If you only need the minimum mean length, you may call
     282    /// \ref findMinMean().
     283
     284    /// @{
     285
     286    /// \brief Run the algorithm.
     287    ///
     288    /// This function runs the algorithm.
     289    /// It can be called more than once (e.g. if the underlying digraph
     290    /// and/or the arc lengths have been modified).
     291    ///
     292    /// \return \c true if a directed cycle exists in the digraph.
     293    ///
     294    /// \note <tt>mmc.run()</tt> is just a shortcut of the following code.
     295    /// \code
     296    ///   return mmc.findMinMean() && mmc.findCycle();
     297    /// \endcode
     298    bool run() {
     299      return findMinMean() && findCycle();
     300    }
     301
     302    /// \brief Find the minimum cycle mean.
     303    ///
     304    /// This function finds the minimum mean length of the directed
     305    /// cycles in the digraph.
     306    ///
     307    /// \return \c true if a directed cycle exists in the digraph.
     308    bool findMinMean() {
     309      // Initialization and find strongly connected components
     310      init();
     311      findComponents();
     312     
     313      // Find the minimum cycle mean in the components
     314      for (int comp = 0; comp < _comp_num; ++comp) {
     315        if (!initComponent(comp)) continue;
     316        processRounds();
     317       
     318        // Update the best cycle (global minimum mean cycle)
     319        if ( _curr_found && (!_best_found ||
     320             _curr_length * _best_size < _best_length * _curr_size) ) {
     321          _best_found = true;
     322          _best_length = _curr_length;
     323          _best_size = _curr_size;
     324          _best_node = _curr_node;
     325          _best_level = _curr_level;
     326        }
     327      }
     328      return _best_found;
     329    }
     330
     331    /// \brief Find a minimum mean directed cycle.
     332    ///
     333    /// This function finds a directed cycle of minimum mean length
     334    /// in the digraph using the data computed by findMinMean().
     335    ///
     336    /// \return \c true if a directed cycle exists in the digraph.
     337    ///
     338    /// \pre \ref findMinMean() must be called before using this function.
     339    bool findCycle() {
     340      if (!_best_found) return false;
     341      IntNodeMap reached(_gr, -1);
     342      int r = _best_level + 1;
     343      Node u = _best_node;
     344      while (reached[u] < 0) {
     345        reached[u] = --r;
     346        u = _gr.source(_data[u][r].pred);
     347      }
     348      r = reached[u];
     349      Arc e = _data[u][r].pred;
     350      _cycle_path->addFront(e);
     351      _best_length = _length[e];
     352      _best_size = 1;
     353      Node v;
     354      while ((v = _gr.source(e)) != u) {
     355        e = _data[v][--r].pred;
     356        _cycle_path->addFront(e);
     357        _best_length += _length[e];
     358        ++_best_size;
     359      }
     360      return true;
     361    }
     362
     363    /// @}
     364
     365    /// \name Query Functions
     366    /// The results of the algorithm can be obtained using these
     367    /// functions.\n
     368    /// The algorithm should be executed before using them.
     369
     370    /// @{
     371
     372    /// \brief Return the total length of the found cycle.
     373    ///
     374    /// This function returns the total length of the found cycle.
     375    ///
     376    /// \pre \ref run() or \ref findMinMean() must be called before
     377    /// using this function.
     378    LargeValue cycleLength() const {
     379      return _best_length;
     380    }
     381
     382    /// \brief Return the number of arcs on the found cycle.
     383    ///
     384    /// This function returns the number of arcs on the found cycle.
     385    ///
     386    /// \pre \ref run() or \ref findMinMean() must be called before
     387    /// using this function.
     388    int cycleArcNum() const {
     389      return _best_size;
     390    }
     391
     392    /// \brief Return the mean length of the found cycle.
     393    ///
     394    /// This function returns the mean length of the found cycle.
     395    ///
     396    /// \note <tt>alg.cycleMean()</tt> is just a shortcut of the
     397    /// following code.
     398    /// \code
     399    ///   return static_cast<double>(alg.cycleLength()) / alg.cycleArcNum();
     400    /// \endcode
     401    ///
     402    /// \pre \ref run() or \ref findMinMean() must be called before
     403    /// using this function.
     404    double cycleMean() const {
     405      return static_cast<double>(_best_length) / _best_size;
     406    }
     407
     408    /// \brief Return the found cycle.
     409    ///
     410    /// This function returns a const reference to the path structure
     411    /// storing the found cycle.
     412    ///
     413    /// \pre \ref run() or \ref findCycle() must be called before using
     414    /// this function.
     415    const Path& cycle() const {
     416      return *_cycle_path;
     417    }
     418
     419    ///@}
     420
     421  private:
     422
     423    // Initialization
     424    void init() {
     425      if (!_cycle_path) {
     426        _local_path = true;
     427        _cycle_path = new Path;
     428      }
     429      _cycle_path->clear();
     430      _best_found = false;
     431      _best_length = 0;
     432      _best_size = 1;
     433      _cycle_path->clear();
     434      for (NodeIt u(_gr); u != INVALID; ++u)
     435        _data[u].clear();
     436    }
     437
     438    // Find strongly connected components and initialize _comp_nodes
     439    // and _out_arcs
     440    void findComponents() {
     441      _comp_num = stronglyConnectedComponents(_gr, _comp);
     442      _comp_nodes.resize(_comp_num);
     443      if (_comp_num == 1) {
     444        _comp_nodes[0].clear();
     445        for (NodeIt n(_gr); n != INVALID; ++n) {
     446          _comp_nodes[0].push_back(n);
     447          _out_arcs[n].clear();
     448          for (OutArcIt a(_gr, n); a != INVALID; ++a) {
     449            _out_arcs[n].push_back(a);
     450          }
     451        }
     452      } else {
     453        for (int i = 0; i < _comp_num; ++i)
     454          _comp_nodes[i].clear();
     455        for (NodeIt n(_gr); n != INVALID; ++n) {
     456          int k = _comp[n];
     457          _comp_nodes[k].push_back(n);
     458          _out_arcs[n].clear();
     459          for (OutArcIt a(_gr, n); a != INVALID; ++a) {
     460            if (_comp[_gr.target(a)] == k) _out_arcs[n].push_back(a);
     461          }
     462        }
     463      }
     464    }
     465
     466    // Initialize path data for the current component
     467    bool initComponent(int comp) {
     468      _nodes = &(_comp_nodes[comp]);
     469      int n = _nodes->size();
     470      if (n < 1 || (n == 1 && _out_arcs[(*_nodes)[0]].size() == 0)) {
     471        return false;
     472      }     
     473      for (int i = 0; i < n; ++i) {
     474        _data[(*_nodes)[i]].resize(n + 1);
     475      }
     476      return true;
     477    }
     478
     479    // Process all rounds of computing path data for the current component.
     480    // _data[v][k] is the length of a shortest directed walk from the root
     481    // node to node v containing exactly k arcs.
     482    void processRounds() {
     483      Node start = (*_nodes)[0];
     484      _data[start][0] = PathData(true, 0);
     485      _process.clear();
     486      _process.push_back(start);
     487
     488      int k, n = _nodes->size();
     489      int next_check = 4;
     490      bool terminate = false;
     491      for (k = 1; k <= n && int(_process.size()) < n && !terminate; ++k) {
     492        processNextBuildRound(k);
     493        if (k == next_check || k == n) {
     494          terminate = checkTermination(k);
     495          next_check = next_check * 3 / 2;
     496        }
     497      }
     498      for ( ; k <= n && !terminate; ++k) {
     499        processNextFullRound(k);
     500        if (k == next_check || k == n) {
     501          terminate = checkTermination(k);
     502          next_check = next_check * 3 / 2;
     503        }
     504      }
     505    }
     506
     507    // Process one round and rebuild _process
     508    void processNextBuildRound(int k) {
     509      std::vector<Node> next;
     510      Node u, v;
     511      Arc e;
     512      LargeValue d;
     513      for (int i = 0; i < int(_process.size()); ++i) {
     514        u = _process[i];
     515        for (int j = 0; j < int(_out_arcs[u].size()); ++j) {
     516          e = _out_arcs[u][j];
     517          v = _gr.target(e);
     518          d = _data[u][k-1].dist + _length[e];
     519          if (!_data[v][k].found) {
     520            next.push_back(v);
     521            _data[v][k] = PathData(true, _data[u][k-1].dist + _length[e], e);
     522          }
     523          else if (_tolerance.less(d, _data[v][k].dist)) {
     524            _data[v][k] = PathData(true, d, e);
     525          }
     526        }
     527      }
     528      _process.swap(next);
     529    }
     530
     531    // Process one round using _nodes instead of _process
     532    void processNextFullRound(int k) {
     533      Node u, v;
     534      Arc e;
     535      LargeValue d;
     536      for (int i = 0; i < int(_nodes->size()); ++i) {
     537        u = (*_nodes)[i];
     538        for (int j = 0; j < int(_out_arcs[u].size()); ++j) {
     539          e = _out_arcs[u][j];
     540          v = _gr.target(e);
     541          d = _data[u][k-1].dist + _length[e];
     542          if (!_data[v][k].found || _tolerance.less(d, _data[v][k].dist)) {
     543            _data[v][k] = PathData(true, d, e);
     544          }
     545        }
     546      }
     547    }
     548   
     549    // Check early termination
     550    bool checkTermination(int k) {
     551      typedef std::pair<int, int> Pair;
     552      typename GR::template NodeMap<Pair> level(_gr, Pair(-1, 0));
     553      typename GR::template NodeMap<LargeValue> pi(_gr);
     554      int n = _nodes->size();
     555      LargeValue length;
     556      int size;
     557      Node u;
     558     
     559      // Search for cycles that are already found
     560      _curr_found = false;
     561      for (int i = 0; i < n; ++i) {
     562        u = (*_nodes)[i];
     563        if (!_data[u][k].found) continue;
     564        for (int j = k; j >= 0; --j) {
     565          if (level[u].first == i && level[u].second > 0) {
     566            // A cycle is found
     567            length = _data[u][level[u].second].dist - _data[u][j].dist;
     568            size = level[u].second - j;
     569            if (!_curr_found || length * _curr_size < _curr_length * size) {
     570              _curr_length = length;
     571              _curr_size = size;
     572              _curr_node = u;
     573              _curr_level = level[u].second;
     574              _curr_found = true;
     575            }
     576          }
     577          level[u] = Pair(i, j);
     578          u = _gr.source(_data[u][j].pred);
     579        }
     580      }
     581
     582      // If at least one cycle is found, check the optimality condition
     583      LargeValue d;
     584      if (_curr_found && k < n) {
     585        // Find node potentials
     586        for (int i = 0; i < n; ++i) {
     587          u = (*_nodes)[i];
     588          pi[u] = std::numeric_limits<LargeValue>::max();
     589          for (int j = 0; j <= k; ++j) {
     590            d = _data[u][j].dist * _curr_size - j * _curr_length;
     591            if (_data[u][j].found && _tolerance.less(d, pi[u])) {
     592              pi[u] = d;
     593            }
     594          }
     595        }
     596
     597        // Check the optimality condition for all arcs
     598        bool done = true;
     599        for (ArcIt a(_gr); a != INVALID; ++a) {
     600          if (_tolerance.less(_length[a] * _curr_size - _curr_length,
     601                              pi[_gr.target(a)] - pi[_gr.source(a)]) ) {
     602            done = false;
     603            break;
     604          }
     605        }
     606        return done;
     607      }
     608      return (k == n);
     609    }
     610
     611  }; //class HartmannOrlin
     612
     613  ///@}
     614
     615} //namespace lemon
     616
     617#endif //LEMON_HARTMANN_ORLIN_H
  • test/min_mean_cycle_test.cc

    diff --git a/test/min_mean_cycle_test.cc b/test/min_mean_cycle_test.cc
    a b  
    2626#include <lemon/concept_check.h>
    2727
    2828#include <lemon/karp.h>
     29#include <lemon/hartmann_orlin.h>
    2930#include <lemon/howard.h>
    3031
    3132#include "test_tools.h"
     
    150151    checkConcept< MmcClassConcept<GR, float>,
    151152                  Karp<GR, concepts::ReadMap<GR::Arc, float> > >();
    152153   
     154    // HartmannOrlin
     155    checkConcept< MmcClassConcept<GR, int>,
     156                  HartmannOrlin<GR, concepts::ReadMap<GR::Arc, int> > >();
     157    checkConcept< MmcClassConcept<GR, float>,
     158                  HartmannOrlin<GR, concepts::ReadMap<GR::Arc, float> > >();
     159   
    153160    // Howard
    154161    checkConcept< MmcClassConcept<GR, int>,
    155162                  Howard<GR, concepts::ReadMap<GR::Arc, int> > >();
     
    189196    checkMmcAlg<Karp<GR, IntArcMap> >(gr, l3, c3,  0, 1);
    190197    checkMmcAlg<Karp<GR, IntArcMap> >(gr, l4, c4, -1, 1);
    191198
     199    // HartmannOrlin
     200    checkMmcAlg<HartmannOrlin<GR, IntArcMap> >(gr, l1, c1,  6, 3);
     201    checkMmcAlg<HartmannOrlin<GR, IntArcMap> >(gr, l2, c2,  5, 2);
     202    checkMmcAlg<HartmannOrlin<GR, IntArcMap> >(gr, l3, c3,  0, 1);
     203    checkMmcAlg<HartmannOrlin<GR, IntArcMap> >(gr, l4, c4, -1, 1);
     204
    192205    // Howard
    193206    checkMmcAlg<Howard<GR, IntArcMap> >(gr, l1, c1,  6, 3);
    194207    checkMmcAlg<Howard<GR, IntArcMap> >(gr, l2, c2,  5, 2);