eg_min_cut.c

Go to the documentation of this file.
00001 /* EGlib "Efficient General Library" provides some basic structures and
00002  * algorithms commons in many optimization algorithms.
00003  *
00004  * Copyright (C) 2005 Daniel Espinoza and Marcos Goycoolea.
00005  * 
00006  * This library is free software; you can redistribute it and/or modify it
00007  * under the terms of the GNU Lesser General Public License as published by the
00008  * Free Software Foundation; either version 2.1 of the License, or (at your
00009  * option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful, but 
00012  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
00013  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
00014  * License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this library; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
00019  * */
00020 /** @file 
00021  * @ingroup EGalgMinCut */
00022 /** @addtogroup EGalgMinCut */
00023 /** @{ */
00024 /* ========================================================================= */
00025 #include "eg_min_cut.h"
00026 /* ========================================================================= */
00027 /** @brief variables and macros to do the profiling for the algorithm, it
00028  * include counting the impact of all shrinking steps, the number of
00029  * improvements cuts found along the way, and how many times where the main
00030  * functions called. */
00031 /** @{ */
00032 #if __MC_PROFILE_ <= DEBUG
00033 unsigned long long __MC_profile_lvl[5] = { 0, 0, 0, 0, 0 };
00034                                                      /**< shrinkings per level*/
00035 unsigned long long __MC_profile_tn = 0;/**<Number of calls to #EGalgMCtestNode*/
00036 unsigned long long __MC_profile_up = 0;/**< Number of improving cuts found */
00037 #define UPDATE(counter) (counter)++
00038 #else
00039 #define UPDATE(counter)
00040 #endif
00041 /** @} */
00042 /* ========================================================================= */
00043 /** @brief Expand all nodes shrinked within a node, and store their id-s
00044  * (#EGalgMCnode_t::id) in the given array (including the node itself ).
00045  * @param cut array where to store the cut (its size should be at least
00046  * #EGsrkNode_t::mmb_sz + 1).
00047  * @param N node to expand.
00048  * @return zero on success, non-zero otherwise.
00049  * */
00050 static inline int EGalgMCexpandNode (unsigned int *const cut,
00051                                      EGalgMCnode_t * const N)
00052 {
00053   unsigned int register i = N->node.mmb_sz;
00054   EGeList_t *const n_end = &(N->node.members);
00055   EGeList_t *n_it = n_end->next;
00056   EGalgMCnode_t *M;
00057   cut[i] = N->id;
00058   for (; n_it != n_end; n_it = n_it->next)
00059   {
00060     M = EGcontainerOf (n_it, EGalgMCnode_t, node.members);
00061     cut[--i] = M->id;
00062   }
00063   return 0;
00064 }
00065 
00066 /* ========================================================================= */
00067 /** @brief Given a node, test if the node is a better cut (by itself) then the
00068  * currently found, and if so, update the current best cut, and call the
00069  * callback if it is non-NULL.
00070  * @param G current min-cut graph.
00071  * @param cb callback structure.
00072  * @param N node to test.
00073  * @return zero on success, non-zero otherwise. 
00074  * */
00075 static inline int EGalgMCtestNode (EGalgMCgraph_t * const G,
00076                                    EGalgMCcbk_t * const cb,
00077                                    EGalgMCnode_t * const N)
00078 {
00079   int rval = 0;
00080   UPDATE (__MC_profile_tn);
00081   TESTL (__MC_DEBUG_, EGlpNumIsLess (N->node.weight, zeroLpNum),
00082          "node %u with " "negative weight %lf", N->id,
00083          EGlpNumToLf (N->node.weight));
00084   if (EGlpNumIsLess (N->node.weight, G->cut_val))
00085   {
00086     UPDATE (__MC_profile_up);
00087     MESSAGE (__MC_VRBLVL_, "Update cut_val from %lf to %lf, n_nodes %u"
00088              " n_edges %u", EGlpNumToLf (G->cut_val),
00089              EGlpNumToLf (N->node.weight), G->G.G.n_nodes, G->G.G.n_edges);
00090     EGlpNumCopy (G->cut_val, N->node.weight);
00091     G->cut_sz = N->node.mmb_sz + 1;
00092     rval = EGalgMCexpandNode (G->cut, N);
00093     CHECKRVALG (rval, CLEANUP);
00094     if (cb && EGlpNumIsLess (N->node.weight, cb->cutoff))
00095     {
00096       rval = cb->do_fn (G->cut_val, G->cut_sz, G->cut, cb->param);
00097       CHECKRVALG (rval, CLEANUP);
00098     }
00099   }
00100   else if (cb && EGlpNumIsLess (N->node.weight, cb->cutoff))
00101   {
00102     unsigned int *cut = EGsMalloc (unsigned int, N->node.mmb_sz + 1);
00103     rval = EGalgMCexpandNode (cut, N);
00104     CHECKRVALG (rval, CLEANUP);
00105     rval = cb->do_fn (N->node.weight, N->node.mmb_sz + 1, cut, cb->param);
00106     CHECKRVALG (rval, CLEANUP);
00107     EGfree (cut);
00108   }
00109   /* ending */
00110 CLEANUP:
00111   return rval;
00112 }
00113 
00114 /* ========================================================================= */
00115 /** @brief Set all #EGalgMCnode_t::hit fields of all neighbours of the given
00116  * node.
00117  * @param NODE node whose neighbours we are going to hit.
00118  *
00119  * Note that we assume tha there exist variables (not in use within the scope
00120  * of this call) as e_it, e_end, lep, o_type, M and E.
00121  * */
00122 #define _EGalgMChitNeighbours(NODE) {\
00123   e_end = &((NODE)->node.node.edges);\
00124   for( e_it = e_end->next; e_it != e_end; e_it = e_it->next)\
00125     {\
00126       lep = EGcontainerOf(e_it, EGeUgraphEP_t,cn);\
00127       o_type = lep->type ? 0U : 1U;\
00128       E = EGcontainerOf(lep, EGalgMCedge_t, edge.edge.ep[lep->type]);\
00129       M = EGcontainerOf(E->edge.edge.ep[o_type].node, EGalgMCnode_t, node.node);\
00130       M->hit = &(E->edge);\
00131     }}
00132 
00133 /* ========================================================================= */
00134 /** @brief Set all #EGalgMCnode_t::hit fields of all neighbours of the given
00135  * node.
00136  * @param NODE node whose neighbours we are going to hit.
00137  *
00138  * Note that we assume tha there exist variables (not in use within the scope
00139  * of this call) as e_it, e_end, lep, o_type, M and E.
00140  * */
00141 #define _EGalgMCunhitNeighbours(NODE) {\
00142   e_end = &((NODE)->node.node.edges);\
00143   for( e_it = e_end->next; e_it != e_end; e_it = e_it->next)\
00144     {\
00145       lep = EGcontainerOf(e_it, EGeUgraphEP_t,cn);\
00146       o_type = lep->type ? 0U : 1U;\
00147       E = EGcontainerOf(lep, EGalgMCedge_t, edge.edge.ep[lep->type]);\
00148       M = EGcontainerOf(E->edge.edge.ep[o_type].node, EGalgMCnode_t, node.node);\
00149       M->hit = 0;\
00150     }}
00151 
00152 /* ========================================================================= */
00153 int EGalgMCidentifyPRedges (EGalgMCgraph_t * const G,
00154                             EGalgMCcbk_t * const cb,
00155                             const unsigned int max_lvl)
00156 {
00157   /* local variables */
00158   int rval = 0;
00159   EGeList_t *e_it,
00160    *e_end,
00161    *e2_it,
00162    *e3_it,
00163    *e2_end,
00164    *c_lvl;
00165   unsigned int o_type,
00166     o_type2,
00167     o_type3;
00168   EGalgMCnode_t *N,
00169    *M,
00170    *X,
00171    *Y;
00172   EGalgMCedge_t *E,
00173    *F,
00174    *H;
00175   EGeUgraphEP_t *lep,
00176    *lep2,
00177    *lep3;
00178   /* N_dv store the delta of N over two minus G->epsilon, and the same is true 
00179    * for M_dv, but for node M, the N_dv2 and M_dv2 are used to speed-up the
00180    * second PR test. */
00181   EGlpNum_t N_dv2,
00182     M_dv2,
00183     N_dv,
00184     M_dv;
00185   EGlpNumInitVar (N_dv2);
00186   EGlpNumInitVar (M_dv2);
00187   EGlpNumInitVar (N_dv);
00188   EGlpNumInitVar (M_dv);
00189 REDO:
00190   /* ======================================================================= */
00191   /* we start doing test on list level 0 */
00192   MESSAGE (__MC_VRBLVL_, "PR shrinking level 0, nodes %u edges %u",
00193            G->G.G.n_nodes, G->G.G.n_edges);
00194   c_lvl = G->lvl_list;
00195   while (!EGeListIsEmpty (c_lvl) && EGlpNumIsLess (zeroLpNum, G->cut_val))
00196   {
00197     N = EGcontainerOf (c_lvl->next, EGalgMCnode_t, lvl_cn);
00198     rval = EGalgMCtestNode (G, cb, N);
00199     CHECKRVALG (rval, CLEANUP);
00200     N->lvl = 1;
00201     EGeListMoveAfter (&(N->lvl_cn), c_lvl + 1);
00202   }
00203   /* ======================================================================= */
00204   /* now we start doing the first level test */
00205   if (max_lvl < 1 || G->G.G.n_nodes < 3 ||
00206       EGlpNumIsEqual (zeroLpNum, G->cut_val, G->epsilon))
00207     goto CLEANUP;
00208   MESSAGE (__MC_VRBLVL_, "PR shrinking level 1, nodes %u edges %u",
00209            G->G.G.n_nodes, G->G.G.n_edges);
00210   c_lvl++;
00211   while (!EGeListIsEmpty (c_lvl) && G->G.G.n_nodes > 2)
00212   {
00213     N = EGcontainerOf (c_lvl->next, EGalgMCnode_t, lvl_cn);
00214     TESTL (__MC_DEBUG_, EGlpNumIsLess (N->node.weight, zeroLpNum),
00215            "node %u with " "negative weight %lf", N->id,
00216            EGlpNumToLf (N->node.weight));
00217     EGlpNumCopy (N_dv, N->node.weight);
00218     EGlpNumDivUiTo (N_dv, 2);
00219     EGlpNumSubTo (N_dv, G->epsilon);
00220     e_end = &(N->node.node.edges);
00221     for (e_it = e_end->next; e_it != e_end; e_it = e_it->next)
00222     {
00223       lep = EGcontainerOf (e_it, EGeUgraphEP_t, cn);
00224       o_type = lep->type ? 0U : 1U;
00225       E = EGcontainerOf (lep, EGalgMCedge_t, edge.edge.ep[lep->type]);
00226       M =
00227         EGcontainerOf (E->edge.edge.ep[o_type].node, EGalgMCnode_t, node.node);
00228       /* if this node is done processing (first condition) or if it is waiting
00229        * a higher value test level, we should skip it */
00230       if (M->lvl > 1)
00231         continue;
00232       /* otherwise we should make the test */
00233       TESTL (__MC_DEBUG_, EGlpNumIsLess (M->node.weight, zeroLpNum),
00234              "node %u with " "negative weight %lf", M->id,
00235              EGlpNumToLf (M->node.weight));
00236       EGlpNumCopy (M_dv, M->node.weight);
00237       EGlpNumDivUiTo (M_dv, 2);
00238       EGlpNumSubTo (M_dv, G->epsilon);
00239       /* check if edge E is shrinkable, if so, shrink these two nodes, and
00240        * update the new node level, and restart the testing phase */
00241       TESTL (__MC_DEBUG_, EGlpNumIsLess (E->edge.weight, zeroLpNum),
00242              "edge %u with " "negative weight %lf", E->id,
00243              EGlpNumToLf (E->edge.weight));
00244       if (EGlpNumIsLeq (N_dv, E->edge.weight)
00245           || EGlpNumIsLeq (M_dv, E->edge.weight))
00246       {
00247         EGalgMCidentifyNodes (G, N, M);
00248         UPDATE (__MC_profile_lvl[1]);
00249         TESTL (__MC_DEBUG_, EGlpNumIsLess (N->node.weight, zeroLpNum),
00250                "node %u" " with negative weight %lf", N->id,
00251                EGlpNumToLf (N->node.weight));
00252         goto REDO;
00253       }
00254     }
00255     /* move node to next level */
00256     N->lvl = 2;
00257     EGeListMoveAfter (&(N->lvl_cn), c_lvl + 1);
00258   }
00259   /* ======================================================================= */
00260   /* now we start performing test level two */
00261   if (max_lvl < 2 || G->G.G.n_nodes < 3)
00262     goto CLEANUP;
00263   MESSAGE (__MC_VRBLVL_, "PR shrinking level 2, nodes %u edges %u",
00264            G->G.G.n_nodes, G->G.G.n_edges);
00265   c_lvl++;
00266   while (!EGeListIsEmpty (c_lvl) && G->G.G.n_nodes > 2)
00267   {
00268     N = EGcontainerOf (c_lvl->next, EGalgMCnode_t, lvl_cn);
00269     TESTL (__MC_DEBUG_, EGlpNumIsLess (N->node.weight, zeroLpNum),
00270            "node %u with " "negative weight %lf", N->id,
00271            EGlpNumToLf (N->node.weight));
00272     EGlpNumCopy (N_dv, N->node.weight);
00273     EGlpNumDivUiTo (N_dv, 2);
00274     EGlpNumSubTo (N_dv, G->epsilon);
00275     e_end = &(N->node.node.edges);
00276     /* first hit all relevant neighbours of N */
00277     _EGalgMChitNeighbours (N);
00278     /* and now, for all neighbour M of N, find a common nwighbour X */
00279     for (e_it = e_end->next; e_it != e_end; e_it = e_it->next)
00280     {
00281       lep = EGcontainerOf (e_it, EGeUgraphEP_t, cn);
00282       o_type = lep->type ? 0U : 1U;
00283       E = EGcontainerOf (lep, EGalgMCedge_t, edge.edge.ep[lep->type]);
00284       M =
00285         EGcontainerOf (E->edge.edge.ep[o_type].node, EGalgMCnode_t, node.node);
00286       TESTL (__MC_DEBUG_, EGlpNumIsLess (M->node.weight, zeroLpNum),
00287              "node %u with " "negative weight %lf", M->id,
00288              EGlpNumToLf (M->node.weight));
00289       EGlpNumCopyDiff (N_dv2, N_dv, E->edge.weight);
00290       /* if this node is done processing (first condition) or if it is waiting
00291        * a higher value test level, we should skip it */
00292       if (M->lvl > 2)
00293         continue;
00294       TESTL (__MC_DEBUG_, EGlpNumIsLess (E->edge.weight, zeroLpNum),
00295              "edge %u with " "negative weight %lf", E->id,
00296              EGlpNumToLf (E->edge.weight));
00297       EGlpNumCopy (M_dv, M->node.weight);
00298       EGlpNumDivUiTo (M_dv, 2);
00299       EGlpNumSubTo (M_dv, G->epsilon);
00300       EGlpNumSubTo (M_dv, E->edge.weight);
00301       e2_end = &(M->node.node.edges);
00302       /* now for all common neighbours (i.e. triangles) */
00303       for (e2_it = e2_end->next; e2_it != e2_end; e2_it = e2_it->next)
00304       {
00305         lep2 = EGcontainerOf (e2_it, EGeUgraphEP_t, cn);
00306         o_type2 = lep2->type ? 0U : 1U;
00307         F = EGcontainerOf (lep2, EGalgMCedge_t, edge.edge.ep[lep2->type]);
00308         X =
00309           EGcontainerOf (F->edge.edge.ep[o_type2].node, EGalgMCnode_t,
00310                          node.node);
00311         /* check the condition, first if X is a common neighbour, and if so,
00312          * if the weight conditions for shrinking hold, if so, do the
00313          * apropiate update. */
00314         if (X->hit && EGlpNumIsLeq (N_dv2, X->hit->weight) &&
00315             EGlpNumIsLeq (M_dv, F->edge.weight))
00316         {
00317           EGalgMCidentifyNodes (G, N, M);
00318           UPDATE (__MC_profile_lvl[2]);
00319           TESTL (__MC_DEBUG_, EGlpNumIsLess (N->node.weight, zeroLpNum),
00320                  "node %u" " with negative weight %lf", N->id,
00321                  EGlpNumToLf (N->node.weight));
00322           goto REDO;
00323         }
00324       }
00325     }
00326     /* ending, for that, we un-hit all neighbours of N */
00327     _EGalgMCunhitNeighbours (N);
00328     /* move this node to the next level */
00329     N->lvl = 3;
00330     EGeListMoveAfter (&(N->lvl_cn), c_lvl + 1);
00331   }
00332   /* ======================================================================= */
00333   /* now we start performing test level three */
00334   if (max_lvl < 3 || G->G.G.n_nodes < 3)
00335     goto CLEANUP;
00336   MESSAGE (__MC_VRBLVL_, "PR shrinking level 3, nodes %u edges %u",
00337            G->G.G.n_nodes, G->G.G.n_edges);
00338   c_lvl++;
00339   while (!EGeListIsEmpty (c_lvl) && G->G.G.n_nodes > 2)
00340   {
00341     N = EGcontainerOf (c_lvl->next, EGalgMCnode_t, lvl_cn);
00342     e_end = &(N->node.node.edges);
00343     /* first hit all relevant neighbours of N */
00344     _EGalgMChitNeighbours (N);
00345     /* and now, for all neighbour M of N, compute lb_{NM} stored in N_dv */
00346     for (e_it = e_end->next; e_it != e_end; e_it = e_it->next)
00347     {
00348       lep = EGcontainerOf (e_it, EGeUgraphEP_t, cn);
00349       o_type = lep->type ? 0U : 1U;
00350       E = EGcontainerOf (lep, EGalgMCedge_t, edge.edge.ep[lep->type]);
00351       M =
00352         EGcontainerOf (E->edge.edge.ep[o_type].node, EGalgMCnode_t, node.node);
00353       EGlpNumCopyDiff (N_dv, E->edge.weight, G->epsilon);
00354       /* if this node is done processing (first condition) or if it is waiting
00355        * a higher value test level, we should skip it */
00356       if (M->lvl > 3)
00357         continue;
00358       e2_end = &(M->node.node.edges);
00359       /* now for all common neighbours (i.e. triangles) */
00360       for (e2_it = e2_end->next; e2_it != e2_end; e2_it = e2_it->next)
00361       {
00362         lep2 = EGcontainerOf (e2_it, EGeUgraphEP_t, cn);
00363         o_type2 = lep2->type ? 0U : 1U;
00364         F = EGcontainerOf (lep2, EGalgMCedge_t, edge.edge.ep[lep2->type]);
00365         X =
00366           EGcontainerOf (F->edge.edge.ep[o_type2].node, EGalgMCnode_t,
00367                          node.node);
00368         /* check the condition, first if X is a common neighbour, and if so,
00369          * if the weight conditions for shrinking hold, if so, do the
00370          * apropiate update. */
00371         if (X->hit)
00372         {
00373           if (EGlpNumIsLess (X->hit->weight, F->edge.weight))
00374             EGlpNumAddTo (N_dv, X->hit->weight);
00375           else
00376             EGlpNumAddTo (N_dv, F->edge.weight);
00377         }
00378       }                         /* end e2_it */
00379       /* check condition, and do the shrinking if necesary */
00380       if (EGlpNumIsLeq (G->cut_val, N_dv))
00381       {
00382         EGalgMCidentifyNodes (G, N, M);
00383         UPDATE (__MC_profile_lvl[3]);
00384         TESTL (__MC_DEBUG_, EGlpNumIsLess (N->node.weight, zeroLpNum),
00385                "node %u" " with negative weight %lf", N->id,
00386                EGlpNumToLf (N->node.weight));
00387         goto REDO;
00388       }
00389     }
00390     /* ending this level test for N, for that, we un-hit all neighbours of N */
00391     _EGalgMCunhitNeighbours (N);
00392     /* move this node to the next level */
00393     N->lvl = 4;
00394     EGeListMoveAfter (&(N->lvl_cn), c_lvl + 1);
00395   }
00396   /* ======================================================================= */
00397   /* now we start performing test level four */
00398   if (max_lvl < 4 || G->G.G.n_nodes < 3)
00399     goto CLEANUP;
00400   MESSAGE (__MC_VRBLVL_, "PR shrinking level 4, nodes %u edges %u",
00401            G->G.G.n_nodes, G->G.G.n_edges);
00402   c_lvl++;
00403   while (!EGeListIsEmpty (c_lvl) && G->G.G.n_nodes > 2)
00404   {
00405     N = EGcontainerOf (c_lvl->next, EGalgMCnode_t, lvl_cn);
00406     MESSAGE (__MC_VRBLVL_, "Looking at node %u, level %u", N->id, N->lvl);
00407     e_end = &(N->node.node.edges);
00408     /* first hit all relevant neighbours of N */
00409     _EGalgMChitNeighbours (N);
00410     EGlpNumCopy (N_dv, N->node.weight);
00411     EGlpNumDivUiTo (N_dv, 2);
00412     EGlpNumSubTo (N_dv, G->epsilon);
00413     /* and now, for all neighbour M of N, find two common neighbours X Y */
00414     for (e_it = e_end->next; e_it != e_end; e_it = e_it->next)
00415     {
00416       lep = EGcontainerOf (e_it, EGeUgraphEP_t, cn);
00417       o_type = lep->type ? 0U : 1U;
00418       E = EGcontainerOf (lep, EGalgMCedge_t, edge.edge.ep[lep->type]);
00419       M =
00420         EGcontainerOf (E->edge.edge.ep[o_type].node, EGalgMCnode_t, node.node);
00421       EGlpNumCopyDiff (N_dv2, N_dv, E->edge.weight);
00422       EGlpNumCopy (M_dv, M->node.weight);
00423       EGlpNumDivUiTo (M_dv, 2);
00424       EGlpNumSubTo (M_dv, G->epsilon);
00425       EGlpNumSubTo (M_dv, E->edge.weight);
00426       /* if this node is done processing (first condition) or if it is waiting
00427        * a higher value test level, we should skip it */
00428       if (M->lvl > 4)
00429         continue;
00430       e2_end = &(M->node.node.edges);
00431       /* now for all common neighbours (i.e. triangles) */
00432       for (e2_it = e2_end->next; e2_it != e2_end; e2_it = e2_it->next)
00433       {
00434         lep2 = EGcontainerOf (e2_it, EGeUgraphEP_t, cn);
00435         o_type2 = lep2->type ? 0U : 1U;
00436         F = EGcontainerOf (lep2, EGalgMCedge_t, edge.edge.ep[lep2->type]);
00437         X =
00438           EGcontainerOf (F->edge.edge.ep[o_type2].node, EGalgMCnode_t,
00439                          node.node);
00440         /* check the condition, first if X is a common neighbour, and if so,
00441          * find a second common neighbour */
00442         if (X->hit)
00443           for (e3_it = e2_it->next; e3_it != e2_end; e3_it = e3_it->next)
00444           {
00445             lep3 = EGcontainerOf (e3_it, EGeUgraphEP_t, cn);
00446             o_type3 = lep3->type ? 0U : 1U;
00447             H = EGcontainerOf (lep3, EGalgMCedge_t, edge.edge.ep[lep3->type]);
00448             Y = EGcontainerOf (H->edge.edge.ep[o_type3].node, EGalgMCnode_t,
00449                                node.node);
00450             /* check the condition, first if Y is a common neighbour, and if so,
00451              * test the condition */
00452             if (Y->hit)
00453             {
00454               EGlpNumCopySum (M_dv2, X->hit->weight, Y->hit->weight);
00455               if (EGlpNumIsLess (M_dv2, N_dv2))
00456                 continue;
00457               EGlpNumCopySum (M_dv2, F->edge.weight, H->edge.weight);
00458               if (EGlpNumIsLess (M_dv2, M_dv))
00459                 continue;
00460               if (EGlpNumIsLess (Y->hit->weight, N_dv2) &&
00461                   EGlpNumIsLess (F->edge.weight, M_dv))
00462                 continue;
00463               if (EGlpNumIsLess (X->hit->weight, N_dv2) &&
00464                   EGlpNumIsLess (H->edge.weight, M_dv))
00465                 continue;
00466               /* if we reach this point, we can shrink N and M */
00467               EGalgMCidentifyNodes (G, N, M);
00468               UPDATE (__MC_profile_lvl[4]);
00469               TESTL (__MC_DEBUG_, EGlpNumIsLess (N->node.weight, zeroLpNum),
00470                      "node %u with negative weight %lf", N->id,
00471                      EGlpNumToLf (N->node.weight));
00472               goto REDO;
00473             }                   /* end Y is neighbour */
00474           }                     /* end e3_it */
00475       }                         /* end e2_it */
00476     }                           /* end e_it */
00477     /* ending this level test for N, for that, we un-hit all neighbours of N */
00478     _EGalgMCunhitNeighbours (N);
00479     /* take out his node from the lists */
00480     N->lvl = 5;
00481     MESSAGE (__MC_VRBLVL_, "Erasing at node %u, level %u", N->id, N->lvl);
00482     EGeListDel (&(N->lvl_cn));
00483   }
00484   /* ending */
00485 CLEANUP:
00486   MESSAGE (__MC_VRBLVL_, "PR shrinking level %u, nodes %u edges %u", max_lvl,
00487            G->G.G.n_nodes, G->G.G.n_edges);
00488   EGlpNumClearVar (N_dv2);
00489   EGlpNumClearVar (M_dv2);
00490   EGlpNumClearVar (N_dv);
00491   EGlpNumClearVar (M_dv);
00492   return rval;
00493 }
00494 
00495 /* ========================================================================= */
00496 /** @brief Given a current min cut graph, build a Push-Relabel graph.
00497  * @param mcG pointer to the min cut graph.
00498  * @param prG pointer to the push-relabel graph, it should be previously
00499  * initialized.
00500  * @param node_map array of mappings for nodes in the push-relabel graph, to
00501  * nodes in the min cut graph. It's size should be at least n_nodes.
00502  * @param all_pr_nodes array containing nodes to use in the push-relabel
00503  * graph, all elements should be already initialized, and the array should be
00504  * of size at least n_nodes.
00505  * @param all_pr_edges array contaaining edges to use in the push-relabel
00506  * graph, all elements should be initialized, and the array should be of size
00507  * at least n_edges.
00508  * @return zero on success, non-zero otherwise.
00509  * */
00510 static inline int EGalgMCbuildPRgraph (EGalgMCgraph_t * const mcG,
00511                                        EGalgPRgraph_t * const prG,
00512                                        EGalgMCnode_t ** const node_map,
00513                                        EGalgPRnode_t * const all_pr_nodes,
00514                                        EGalgPRedge_t * const all_pr_edges)
00515 {
00516   int rval = 0;
00517   EGeList_t *e_it,
00518    *e_end,
00519    *n_it,
00520    *n_end;
00521 #if DEBUG
00522   unsigned int const n_pr_nodes = mcG->G.G.n_nodes;
00523   unsigned int const n_pr_edges = mcG->G.G.n_edges;
00524 #endif
00525   EGalgMCnode_t *N,
00526    *M;
00527   EGalgMCedge_t *E;
00528   EGeUgraphEP_t *lep;
00529   unsigned register int i;
00530   MESSAGE (__MC_VRBLVL_,
00531            "Building push-relabel graph on %u nodes and %u edges", n_pr_nodes,
00532            n_pr_edges);
00533   /* building graph */
00534   EGalgPRgraphReset (prG);
00535   n_end = &(mcG->G.G.nodes);
00536   /* add all nodes */
00537   for (i = 0, n_it = n_end->next; n_it != n_end; n_it = n_it->next, i++)
00538   {
00539     N = EGcontainerOf (n_it, EGalgMCnode_t, node.node.node_cn);
00540     EGalgPRnodeReset (all_pr_nodes + i);
00541     EGeDgraphAddNode (&(prG->G), &(all_pr_nodes[i].v));
00542     node_map[i] = N;
00543     N->new_id = i;
00544   }
00545   /* test if we added the right number of nodes */
00546   TESTG ((rval = (i != n_pr_nodes)), CLEANUP, "number of added nodes %u is not"
00547          "the number of nodes in the MinCut graph %u", i, n_pr_nodes);
00548   /* and now add all edges */
00549   for (i = 0, n_it = n_end->next; n_it != n_end; n_it = n_it->next)
00550   {
00551     N = EGcontainerOf (n_it, EGalgMCnode_t, node.node.node_cn);
00552     e_end = &(N->node.node.edges);
00553     for (e_it = e_end->next; e_it != e_end; e_it = e_it->next)
00554     {
00555       lep = EGcontainerOf (e_it, EGeUgraphEP_t, cn);
00556       if (lep->type)
00557         continue;
00558       E = EGcontainerOf (lep, EGalgMCedge_t, edge.edge.ep[0]);
00559       M = EGcontainerOf (E->edge.edge.ep[1].node, EGalgMCnode_t, node.node);
00560       /* we need to add the edge only once */
00561       /* now we add the edge */
00562       EGalgPRedgeReset (all_pr_edges + i);
00563       EGlpNumCopy (all_pr_edges[i].fw.u, E->edge.weight);
00564       EGlpNumCopy (all_pr_edges[i].bw.u, E->edge.weight);
00565       EGeDgraphAddEdge (&(prG->G),
00566                         &(all_pr_nodes[N->new_id].v),
00567                         &(all_pr_nodes[M->new_id].v), &(all_pr_edges[i].fw.e));
00568       EGeDgraphAddEdge (&(prG->G),
00569                         &(all_pr_nodes[M->new_id].v),
00570                         &(all_pr_nodes[N->new_id].v), &(all_pr_edges[i].bw.e));
00571       i++;
00572     }
00573   }
00574   /* test if we added the right number of edges */
00575   TESTG ((rval =
00576           (i != n_pr_edges)), CLEANUP,
00577          "number of added edges %u is not "
00578          "the number of edges in the MinCut graph %u", i, n_pr_edges);
00579 
00580   /* ending */
00581 #if DEBUG
00582 CLEANUP:
00583 #endif
00584   return rval;
00585 }
00586 
00587 /* ========================================================================= */
00588 /** @brief Compute the farthest node in the push-relabel graph from the given
00589  * starting point.
00590  * @param prG push relabel graph.
00591  * @param S source node.
00592  * @param T where to store the farthest node.
00593  * @return zero on success, non-zero otherwise */
00594 static inline int EGalgMCcomputeT (EGalgPRgraph_t * const prG,
00595                                    EGalgPRnode_t * const S,
00596                                    EGalgPRnode_t ** const T)
00597 {
00598   EGeList_t *n_it,
00599    *n_end,
00600    *queue;
00601   EGalgPRnode_t *N = 0,
00602    *P = 0;
00603   EGalgPRse_t *E = 0;
00604   n_end = &(prG->G.nodes);
00605   /* initialize distance, and queue */
00606   for (n_it = n_end->next; n_it != n_end; n_it = n_it->next)
00607   {
00608     N = EGcontainerOf (n_it, EGalgPRnode_t, v.node_cn);
00609     N->LVL_list = (EGeList_t)
00610     {
00611     0, 0};
00612     N->d = UINT_MAX;
00613   }
00614   S->d = 0;
00615   *T = P = S;
00616   queue = &(S->LVL_list);
00617   EGeListInit (queue);
00618   n_end = &(P->v.out_edge);
00619   for (n_it = n_end->next; n_it != n_end; n_it = n_it->next)
00620   {
00621     E = EGcontainerOf (n_it, EGalgPRse_t, e.tail_cn);
00622     N = EGcontainerOf (E->e.head, EGalgPRnode_t, v);
00623     if (N->LVL_list.next)
00624       continue;
00625     EGeListAddAfter (&(N->LVL_list), queue);
00626     N->d = P->d + 1;
00627   }
00628   while (!EGeListIsEmpty (queue))
00629   {
00630     P = EGcontainerOf (queue->prev, EGalgPRnode_t, LVL_list);
00631     EGeListDel (queue->prev);
00632     n_end = &(P->v.out_edge);
00633     for (n_it = n_end->next; n_it != n_end; n_it = n_it->next)
00634     {
00635       E = EGcontainerOf (n_it, EGalgPRse_t, e.tail_cn);
00636       N = EGcontainerOf (E->e.head, EGalgPRnode_t, v);
00637       if (N->LVL_list.next)
00638         continue;
00639       EGeListAddAfter (&(N->LVL_list), queue);
00640       N->d = P->d + 1;
00641     }
00642   }
00643   n_end = &(prG->G.nodes);
00644   for (n_it = n_end->next; n_it != n_end; n_it = n_it->next)
00645   {
00646     N = EGcontainerOf (n_it, EGalgPRnode_t, v.node_cn);
00647     if ((*T)->d < N->d)
00648       *T = N;
00649   }
00650   MESSAGE (__MC_VRBLVL_, "worst distance is %u", (*T)->d);
00651   return 0;
00652 }
00653 
00654 /* ========================================================================= */
00655 int EGalgMC (EGalgMCgraph_t * const G,
00656              EGalgMCcbk_t * const cb,
00657              const unsigned int max_lvl)
00658 {
00659   int rval = 0;
00660   EGalgPRgraph_t prG;
00661   EGalgPRnode_t *all_pr_nodes = 0,
00662    *pr_node,
00663    *pr_node2;
00664   EGalgPRedge_t *all_pr_edges = 0;
00665   EGalgMCnode_t **node_map = 0;
00666   EGalgMCnode_t *N;
00667   unsigned int *const cut = G->cut;
00668   EGeList_t *n_it,
00669    *n_end,
00670    *c_it,
00671    *c_end;
00672   unsigned int n_pr_nodes,
00673     n_pr_edges,
00674     cur_s = 0;
00675   unsigned register int i;
00676   /* start random with seed 1 */
00677   srandom (1);
00678   /* we star by calling the Padberg-Rinaldi shrinking */
00679   rval = EGalgMCidentifyPRedges (G, cb, max_lvl);
00680   CHECKRVALG (rval, CLEANUP);
00681   /* intialize memory to be used by the push-relabel graph */
00682   n_pr_nodes = G->G.G.n_nodes;
00683   n_pr_edges = G->G.G.n_edges;
00684   /* if the reduced graph is empty, we have win */
00685   if (n_pr_nodes < 3 || !n_pr_edges)
00686     goto CLEANUP;
00687   /* otherwise, we have to work */
00688   all_pr_nodes = EGsMalloc (EGalgPRnode_t, n_pr_nodes);
00689   for (i = n_pr_nodes; i--;)
00690     EGalgPRnodeInit (all_pr_nodes + i);
00691   all_pr_edges = EGsMalloc (EGalgPRedge_t, n_pr_edges);
00692   for (i = n_pr_edges; i--;)
00693     EGalgPRedgeInit (all_pr_edges + i);
00694   node_map = EGsMalloc (EGalgMCnode_t *, n_pr_nodes);
00695   EGalgPRgraphInit (&prG);
00696   /* main loop */
00697   while (G->G.G.n_nodes > 2 && G->G.G.n_edges &&
00698          EGlpNumIsLess (zeroLpNum, G->cut_val))
00699   {
00700     /* now we build our first push-relabel graph */
00701     rval = EGalgMCbuildPRgraph (G, &prG, node_map, all_pr_nodes, all_pr_edges);
00702     CHECKRVALG (rval, CLEANUP);
00703     /* do the actual min-st-cut. Note that an heuristic that might help
00704      * speeding up this part is to always peform the minimum s-t cut by
00705      * slecting t to be one at maximum distance from s. thus next time we run
00706      * this code, the distance would be half the previous time. */
00707     /* we first select s randomly from among all nodes in the graph */
00708     cur_s = random () % prG.G.n_nodes;
00709     MESSAGE (__MC_VRBLVL_, "computing t, maximum distance node from s");
00710     rval = EGalgMCcomputeT (&prG, all_pr_nodes + cur_s, &pr_node);
00711     CHECKRVALG (rval, CLEANUP);
00712     MESSAGE (__MC_VRBLVL_, "computing s-t cut");
00713     rval = EGalgPRminSTcut (&prG, all_pr_nodes + cur_s, pr_node);
00714     CHECKRVALG (rval, CLEANUP);
00715     /* check if the cut found improve the minimum cut */
00716     if (EGlpNumIsLess (pr_node->e, G->cut_val))
00717     {
00718       /* if so, we update the best cut */
00719       UPDATE (__MC_profile_up);
00720       MESSAGE (__MC_VRBLVL_, "Update cut_val from %lf to %lf",
00721                EGlpNumToLf (G->cut_val), EGlpNumToLf (pr_node->e));
00722       EGlpNumCopy (G->cut_val, pr_node->e);
00723       c_end = &(prG.G.nodes);
00724       for (c_it = c_end->next, i = 0; c_it != c_end; c_it = c_it->next)
00725       {
00726         pr_node2 = EGcontainerOf (c_it, EGalgPRnode_t, v.node_cn);
00727         if (pr_node2->d >= prG.G.n_nodes)
00728           continue;
00729         N = node_map[pr_node2 - all_pr_nodes];
00730         cut[i++] = N->id;
00731         n_end = &(N->node.members);
00732         for (n_it = n_end->next; n_it != n_end; n_it = n_it->next)
00733         {
00734           N = EGcontainerOf (n_it, EGalgMCnode_t, node.members);
00735           cut[i++] = N->id;
00736         }
00737       }                         /* end computing the cut */
00738       G->cut_sz = i;
00739       /* call the call-back if necesary */
00740       if (cb && EGlpNumIsLess (G->cut_val, cb->cutoff))
00741       {
00742         rval = cb->do_fn (G->cut_val, G->cut_sz, G->cut, cb->param);
00743         CHECKRVALG (rval, CLEANUP);
00744       }                         /* end call-back */
00745     }                           /* end improving test */
00746     else if (cb && EGlpNumIsLess (pr_node->e, cb->cutoff))
00747     {
00748       unsigned int *lcut = EGsMalloc (unsigned int, G->G.n_onodes);
00749       c_end = &(prG.G.nodes);
00750       for (c_it = c_end->next, i = 0; c_it != c_end; c_it = c_it->next)
00751       {
00752         pr_node2 = EGcontainerOf (c_it, EGalgPRnode_t, v.node_cn);
00753         if (pr_node2->d >= prG.G.n_nodes)
00754           continue;
00755         N = node_map[pr_node2 - all_pr_nodes];
00756         lcut[i++] = N->id;
00757         n_end = &(N->node.members);
00758         for (n_it = n_end->next; n_it != n_end; n_it = n_it->next)
00759         {
00760           N = EGcontainerOf (n_it, EGalgMCnode_t, node.members);
00761           lcut[i++] = N->id;
00762         }
00763       }                         /* end computing the cut */
00764       rval = cb->do_fn (pr_node->e, i, lcut, cb->param);
00765       CHECKRVALG (rval, CLEANUP);
00766       EGfree (lcut);
00767 
00768     }
00769     /* now we just shrink s and t, and call the padberg-rinaldy shrink */
00770     UPDATE (__MC_profile_lvl[0]);
00771     TESTL (__MC_DEBUG_,
00772            EGlpNumIsLess (node_map[cur_s]->node.weight, zeroLpNum),
00773            "node %u with negative weight %lf", node_map[cur_s]->id,
00774            EGlpNumToLf (node_map[0]->node.weight));
00775     EGalgMCidentifyNodes (G, node_map[cur_s], node_map[pr_node - all_pr_nodes]);
00776     TESTL (__MC_DEBUG_,
00777            EGlpNumIsLess (node_map[cur_s]->node.weight, zeroLpNum),
00778            "node %u with negative weight %lf", node_map[cur_s]->id,
00779            EGlpNumToLf (node_map[cur_s]->node.weight));
00780     rval = EGalgMCidentifyPRedges (G, cb, max_lvl);
00781     CHECKRVALG (rval, CLEANUP);
00782   }                             /* end main loop */
00783 
00784   /* ending */
00785 CLEANUP:
00786   if (all_pr_nodes)
00787   {
00788     for (i = n_pr_nodes; i--;)
00789       EGalgPRnodeClear (all_pr_nodes + i);
00790     EGfree (all_pr_nodes);
00791   }
00792   if (all_pr_edges)
00793   {
00794     for (i = n_pr_edges; i--;)
00795       EGalgPRedgeClear (all_pr_edges + i);
00796     EGfree (all_pr_edges);
00797   }
00798   if (node_map)
00799     EGfree (node_map);
00800   return rval;
00801 }
00802 
00803 /* ========================================================================= */
00804 /** @}
00805  * end of eg_min_cut.c */

Generated on Mon Jan 30 08:48:53 2006 for EGlib by  doxygen 1.4.5