예제 #1
0
파일: mcxmm.c 프로젝트: BioDAG/align-paths
void test_cross_ratio
(  mclx* mx
)
   {  dim i, j, n = 0
   ;  for (i=0;i<N_COLS(mx);i++)
      {  mclv* v = mx->cols+i
      ;  double selfv = mclvSelf(v)
      ;  for (j=0;j<v->n_ivps;j++)
         {  mclv* w = mclxGetVector(mx, v->ivps[j].idx, EXIT_ON_FAIL, NULL)
         ;  double arc  =  v->ivps[j].val
         ;  double selfw=  mclvSelf(w)
         ;  double cra  =  mclvIdxVal(w, v->vid, NULL)
         ;  double s    =  MCX_MIN(selfv, selfw)
         ;  if (s > arc || s > cra)
            fprintf
            (  stdout
            ,  "%u\t%u\t%g\t%g\t%g\t%g\n"
            ,  (unsigned) v->vid
            ,  (unsigned) w->vid
            ,  arc
            ,  cra
            ,  selfv
            ,  selfw
            )
         ;  n++
      ;  }
      }
      fprintf(stderr, "tested %u entries\n", (unsigned) n)
;  }
예제 #2
0
static ofs get_cls_id
(  long idx
,  const mclx* cl
,  const mclx* cltp
,  long* cls_size
)
   {  mclv* tocl = mclxGetVector(cltp, idx, EXIT_ON_FAIL, NULL), *cls = NULL
   ;  ofs clid = -1
   ;  cls_size[0] = 0
   ;  if (tocl)
      {  clid = tocl->ivps[0].idx
      ;  cls  = mclxGetVector(cl, clid, EXIT_ON_FAIL, NULL)
      ;  cls_size[0] = cls->n_ivps
   ;  }
      return clid
;  }
예제 #3
0
static void erdos_link_together
(  mcxIO* xfout
,  mclx* mx
,  mclv* tails
,  mclv* heads
)
   {  dim v = 0
   ;  mclv* relevant = mclvInit(NULL)
   ;  fprintf(xfout->fp, "(");
   ;  for (v=0;v<tails->n_ivps;v++)
      {  long t = tails->ivps[v].idx
      ;  dim j
      ;  mclv* nb = mclxGetVector(mx, t, EXIT_ON_FAIL, NULL)
      ;  mcldMeet(nb, heads, relevant)
      ;  for (j=0;j<relevant->n_ivps;j++)
         {  if (tab_g)
            {  long u = relevant->ivps[j].idx
            ;  const char* sx = mclTabGet(tab_g, (long) t, NULL)
            ;  const char* sy = mclTabGet(tab_g, (long) u, NULL)
            ;  if (!sx)
               sx = "NAx"
            ;  if (!sy)
               sy = "NAy"
            ;  fprintf(xfout->fp, " (%s %s)", sx, sy)
         ;  }
            else
            fprintf(xfout->fp, " (%ld %ld)", (long) t, (long) relevant->ivps[j].idx)
      ;  }
         if (!relevant->n_ivps)
         mcxErr(me, "odd, %d has no friends\n", (int) t)
   ;  }
      fprintf(xfout->fp, " )\n");
   ;  mclvFree(&relevant)
;  }
예제 #4
0
파일: mcxmm.c 프로젝트: BioDAG/align-paths
mcxstatus fire_node_next
(  const mclx* mx
,  mclv* seen
,  mclv *todo
,  dim start
)
   {  mclv* next = mclvInit(NULL)
   ;  dim i
   ;  mcxstatus s = STATUS_OK
;if(0)fprintf(stderr, "\tnext layer has %d nodes\n", (int) todo->n_ivps)
   ;  for (i=0; i<todo->n_ivps;i++)
      {  mclv* ls = mclxGetVector(mx, todo->ivps[i].idx, RETURN_ON_FAIL, NULL)
      ;  if (ls)
         {  mcldMerge(next, ls, next)
         ;  if (mclvGetIvp(ls, start, NULL))
            {  s = STATUS_FAIL
            ;  break
         ;  }
         }
      }
      mcldMerge(seen, todo, seen)      /* add todo to seen */
   ;  mcldMinus(next, seen, next)      /* remove seen from next */
   ;  mclvCopy(todo, next)             /* copy next to todo */
   ;  mclvFree(&next)
   ;  return s
;  }
예제 #5
0
static int calc_depth
(  mclx* m_transient
)
{   mclx* m_inverse = mclxTranspose(m_transient)
                      ;
    dim c, depth = 0

                   ;
    if(0)puts("")

        ;
    for (c=0; c<N_COLS(m_inverse); c++)
    {   dim this_depth = 0
                         ;
        if (!m_inverse->cols[c].n_ivps)    /* no incoming nodes */
        {   mclv* next = mclxGetVector(m_transient, m_inverse->cols[c].vid, RETURN_ON_FAIL, NULL)
                         ;
            if (!next)
                continue
                ;
            mclgUnionvInitList(m_transient, next)
            ;
            do
            {   mclv* next2 = mclgUnionv(m_transient, next, NULL, SCRATCH_UPDATE, NULL)
                              ;
                if (0 && next->ivps)
                    fprintf(stdout, "chain %d ->\n", (int) m_inverse->cols[c].vid)
                    ,  mclvaDump(next, stdout, -1, " ", 0)
                    ;
                if (this_depth)   /* otherwise starting vector in matrix */
                    mclvFree(&next)
                    ;
                next = next2
                       ;
                this_depth++
                ;
            }
            while (next->n_ivps)
                ;
            mclvFree(&next)      /* did loop at least once, so not the starting vector */
            ;
            mclgUnionvReset(m_transient)
            ;
        }
        if (this_depth > depth)
            depth = this_depth
                    ;
    }

    mclxFree(&m_inverse)
    ;
    return depth
           ;
}
예제 #6
0
파일: mcxmm.c 프로젝트: BioDAG/align-paths
void walk_dag
(  mclx* mx
,  mclv* v
,  int level
)
   {  dim i
   ;  v->val = 2.0
   ;  for (i=0;i<v->n_ivps;i++)
      {  mclv* v = mclxGetVector(mx, v->ivps[i].idx, EXIT_ON_FAIL, NULL)
      ;  v->val = 2.0
      ;  dump_label(tab, v, level)
      ;  
      }
;  }
예제 #7
0
파일: mcxmm.c 프로젝트: BioDAG/align-paths
void dag_diff_select 
(  mclx* mx
,  mclTab* tab
,  mcxIO* xfdiff
,  double child_diff_lq
,  double parent_diff_gq
)
   {  dim i
   ;  mclx* dag = mclxAllocClone(mx)
   ;  for (i=0;i<N_COLS(mx); i++)
      {  mclv* v = mx->cols+i
      ;  dim j
      ;  for (j=0;j<v->n_ivps;j++)
         {  dim idx     = v->ivps[j].idx
         ;  double valv = v->ivps[j].val
         ;  mclv* t = mclxGetVector(mx, idx, EXIT_ON_FAIL, NULL)
         ;  mclp* p = mclvGetIvp(t, v->vid, NULL)
         ;  double valt = p ? p->val : 0.0
         ;  double delta = valv - valt
         ;  double lg = valv, sm = valt
         ;  double child_diff, parent_diff
         ;  int v_is_child = 0

         ;  if (delta < 0)
               delta = -delta
            ,  lg=valt, sm=valv
            ,  v_is_child = 1

         ;  child_diff = sm
         ;  parent_diff = lg

;if(0 && i==111)
fprintf(stderr, "nb %d delta %g\n", (int) idx, delta)
         ;  if (child_diff > child_diff_lq || parent_diff < parent_diff_gq)
            NOTHING
         ;  else
            {  if (v_is_child)
               mclvInsertIdx(dag->cols+i, idx, delta)
            ;  else
               mclvInsertIdx(dag->cols+(t-mx->cols), v->vid, delta)
         ;  }
         }
      }
   ;  mclxWrite(dag, xfdiff, MCLXIO_VALUE_GETENV, EXIT_ON_FAIL)
   ;  mclxFree(&dag)
;  }
예제 #8
0
static void set_cl_to_projection
(  mclMatrix* cl
,  mclMatrix* el_on_cl
)
   {  dim i, j
   ;  for (i=0;i<N_COLS(cl);i++)
      {  mclv* clvec = cl->cols+i
      ;  long  clid  = clvec->vid
      ;  mclv* elclvec = NULL
      ;  mclp* valivp = NULL
      ;  for (j=0;j<clvec->n_ivps;j++)
         {  long elid = clvec->ivps[j].idx
         ;  elclvec = mclxGetVector(el_on_cl, elid, EXIT_ON_FAIL, elclvec)
         ;  valivp = mclvGetIvp(elclvec, clid, NULL)
         ;  if (!valivp && clvec->n_ivps > 1)
            mcxErr("clmCastActors", "match error: el %ld cl %ld", elid, clid)
         ;  clvec->ivps[j].val = valivp ? MCX_MAX(0.01, valivp->val) : 0.01
      ;  }
      }
   }
예제 #9
0
void static do_the_shuffle
(  mclx* mx
,  dim N_shuffle
,  dim* offsets   /* size N_COLS(mx) */
,  dim N_edge
,  dim random_ignore
)
   {  dim n_shuffle = 0
   ;  while (n_shuffle < N_shuffle)
      {  unsigned long rx = (unsigned long) random()
      ;  unsigned long ry = (unsigned long) random()
      ;  mclp* ivpll, *ivplr, *ivprl, *ivprr
      ;  dim edge_x, edge_y, *edge_px, *edge_py
      ;  ofs xro, yro, xlo, ylo = -1, vxo, vyo
      ;  long xl, xr, yl, yr
      ;  mclv* vecxl, *vecxr, *vecyl, *vecyr
      ;  double xlval, xrval, ylval, yrval

      ;  if (rx >= random_ignore || ry >= random_ignore)
         continue

      ;  edge_x = rx % N_edge    /* fixme probably not optimal */
      ;  edge_y = ry % N_edge    /* fixme probably not optimal */

      ;  if (!(edge_px = mcxBsearchFloor(&edge_x, offsets, N_COLS(mx), sizeof edge_x, dimCmp)))
         mcxDie(1, me, "edge %ld not found (max %ld)", (long) edge_x, (long) N_edge)

      ;  if (!(edge_py = mcxBsearchFloor(&edge_y, offsets, N_COLS(mx), sizeof edge_y, dimCmp)))
         mcxDie(1, me, "edge %ld not found (max %ld)", (long) edge_y, (long) N_edge)

      ;  vxo   =  edge_px - offsets
      ;  xl    =  mx->dom_cols->ivps[vxo].idx
      ;  vecxl =  mx->cols+vxo
      ;  xro   =  edge_x - offsets[vxo]

      ;  vyo   =  edge_py - offsets
      ;  yl    =  mx->dom_cols->ivps[vyo].idx
      ;  vecyl =  mx->cols+vyo
      ;  yro   =  edge_y - offsets[vyo]

                        /* Offset computation gone haywire */
      ;  if (xro >= vecxl->n_ivps || yro >= vecyl->n_ivps)     /* note: mixed sign comparison */
         mcxDie(1, me, "paradox 1 in %ld or %ld", xl, yl)

      ;  xr = vecxl->ivps[xro].idx
      ;  yr = vecyl->ivps[yro].idx
      ;  xrval = vecxl->ivps[xro].val
      ;  yrval = vecyl->ivps[yro].val

                        /* Impossible, should have graph */
      ;  vecxr = mclxGetVector(mx, xr, EXIT_ON_FAIL, NULL)
      ;  vecyr = mclxGetVector(mx, yr, EXIT_ON_FAIL, NULL)

                        /* check that we have four different nodes
                         * loops are not present so no need to check those
                        */
      ;  if (xl == yl || xl == yr || xr == yl || xr == yr)
         continue

      ;  if
         (  (0 > (xlo = mclvGetIvpOffset(vecxr, xl, -1)))
         || (0 > (ylo = mclvGetIvpOffset(vecyr, yl, -1)))
         )
         mcxDie
         (  1
         ,  me
         ,  "symmetry violation 1"
            " %ld not found in %ld/%ld OR %ld not found in %ld/%ld"
         ,  (long) xl, (long) vecxr->vid, (long) vecxr->n_ivps
         ,  (long) yl, (long) vecyr->vid, (long) vecyr->n_ivps
         )

                        /* Now:  xl yl :  ivpll
                         *       xl yr :  ivplr
                         *       xr yl :  ivprl
                         *       xr yr :  ivprr
                        */
      ;  xlval = vecxr->ivps[xlo].val
      ;  ylval = vecyr->ivps[ylo].val

      ;  ivpll = mclvGetIvp(vecxl, yl, NULL)
      ;  ivplr = mclvGetIvp(vecxl, yr, NULL)
      ;  ivprl = mclvGetIvp(vecxr, yl, NULL)
      ;  ivprr = mclvGetIvp(vecxr, yr, NULL)

      ;  if
         (  (ivpll && !mclvGetIvp(vecyl, xl, NULL))
         || (ivplr && !mclvGetIvp(vecyr, xl, NULL))
         || (ivprl && !mclvGetIvp(vecyl, xr, NULL))
         || (ivprr && !mclvGetIvp(vecyr, xr, NULL))
         )
         mcxDie(1, me, "symmetry violation 2")

      ;  if ((ivpll && ivplr) || (ivprl && ivprr))
         continue

      ;  {  if (!ivpll && !ivprr) 
            {        /* vecxl <-> xr   becomes vecxl <-> yl
                      * vecxr <-> xl   becomes vecxr <-> yr
                      * vecyl <-> yr   becomes vecyl <-> xl 
                      * vecyr <-> yl   becomes vecyr <-> xr
                     */
            ;  if
               (  mclvReplaceIdx(vecxl, xro, yl, xrval)
               || mclvReplaceIdx(vecyl, yro, xl, xrval)
               || mclvReplaceIdx(vecxr, xlo, yr, ylval)
               || mclvReplaceIdx(vecyr, ylo, xr, ylval)
               )
               mcxDie(1, me, "parallel replacement failure\n")
#if DEBUG
;fprintf(stderr, "parallel edge change remove  %d-%d  %d-%d add  %d-%d  %d-%d\n",
   vecxl->vid, xr, vecyr->vid, yl,  vecxl->vid, yl, vecyr->vid, xr)
#endif
         ;  }
            else if (!ivplr && !ivprl)
            {        /* vecxl -> xr   becomes vecxl <-> yr
                      * vecxr -> xl   becomes vecxr <-> yl
                      * vecyl -> yr   becomes vecyl <-> xr 
                      * vecyr -> yl   becomes vecyr <-> xl
                     */
               if
               (  mclvReplaceIdx(vecxl, xro, yr, xrval)
               || mclvReplaceIdx(vecyr, ylo, xl, xlval)
               || mclvReplaceIdx(vecxr, xlo, yl, yrval)
               || mclvReplaceIdx(vecyl, yro, xr, yrval)
               )
               mcxDie(1, me, "cross replacement failure\n")
#if DEBUG
;fprintf(stderr, "cross edge change remove  %d-%d  %d-%d add  %d-%d  %d-%d\n",
   vecxl->vid, xr, vecyl->vid, yr,  vecxl->vid, yr, vecyl->vid, xr)
#endif
         ;  }
         }
         n_shuffle++
   ;  }
   }
예제 #10
0
void clmDumpNodeScores
(  const char* name
,  mclx* mx
,  mclx* cl
,  mcxenum  mode
)
   {  dim d, e
   ;  clmVScore sc

   ;  if (mode == CLM_NODE_SELF)
      {  for (d=0;d<N_COLS(cl);d++)
         {  ofs o = -1
         ;  dim clsize = cl->cols[d].n_ivps
         ;  for (e=0;e<clsize;e++)
            {  long idx = cl->cols[d].ivps[e].idx
            ;  o = mclxGetVectorOffset(mx, idx, EXIT_ON_FAIL, o)
            ;  mx->cols[o].val = mclvSum(mx->cols+o)              /* fixme stupid dependency. */
            ;  clmVScanDomain(mx->cols+o, cl->cols+d, &sc)
            ;  clm_dump_line
               (name, &sc, idx, cl->cols[d].vid, mx->cols[o].n_ivps, clsize, 0)
         ;  }
         }
         /* fixme: sum_e not set, pbb due to missing clmCastActors */
      }
      else if (mode == CLM_NODE_INCIDENT)
      {  mclx *el_to_cl = NULL
      ;  mclx *el_on_cl = NULL
      ;  mclx *cl_on_cl = NULL
      ;  mclx *cl_on_el = NULL
      ;  clmCastActors
         (&mx, &cl, &el_to_cl, &el_on_cl, &cl_on_cl, &cl_on_el, 0.95)
      ;  mclxFree(&cl_on_cl)
      ;  mclxFree(&cl_on_el)

      ;  for (d=0;d<N_COLS(mx);d++)
         {  long nid  = mx->cols[d].vid
         ;  long nsize = mx->cols[d].n_ivps
         ;  mclv* clidvec = mclxGetVector(el_on_cl, nid, RETURN_ON_FAIL, NULL)
         ;  mclv* clself = mclxGetVector(el_to_cl, nid, RETURN_ON_FAIL, NULL)
         ;  dim f

         ;  if (!clself)
            mcxErr
            ("clmDumpNodeScores panic", "node <%ld> does not belong", nid)

         ;  for (f=0;f<clidvec->n_ivps;f++)
            {  long cid = clidvec->ivps[f].idx
            ;  mclv* clvec = mclxGetVector(cl, cid, RETURN_ON_FAIL, NULL)
                          /* ^ overdoing: cid == clvec->vid */
            ;  int alien
            ;  if (!clvec)
               {  mcxErr
                  (  "clmDumpNodeScores panic"
                  ,  "cluster <%ld> node <%ld> mishap"
                  ,  cid
                  ,  nid
                  )
               ;  continue
            ;  }
               clmVScanDomain(mx->cols+d, clvec, &sc)
            ;  alien = clself && clvec->vid == clself->ivps[0].idx ? 0 : 1
            ;  clm_dump_line
               (name, &sc, nid, clvec->vid, nsize, clvec->n_ivps, alien)
         ;  }
         }
         mclxFree(&el_on_cl)
      ;  mclxFree(&el_to_cl)
   ;  }
   }
예제 #11
0
static dim clm_clm_prune
(  mclx*    mx
,  mclx*    cl
,  dim      prune_sz
,  mclx**   cl_adjustedpp
,  dim*     n_sink
,  dim*     n_source
)
   {  dim d, n_adjusted = 0
   ;  mclx* cl_adj = mclxCopy(cl)
   ;  mclv* cid_affected = mclvClone(cl->dom_cols)
   ;  const char* me = "clmAssimilate"

   ;  double bar_affected = 1.5

   ;  mclx *el_to_cl = NULL
   ;  mclx *el_on_cl = NULL
   ;  mclx *cl_on_cl = NULL
   ;  mclx *cl_on_el = NULL

   ;  *n_sink = 0
   ;  *n_source = 0

   ;  mclvMakeConstant(cid_affected, 1.0)
   ;  mclxColumnsRealign(cl_adj, mclvSizeCmp)

   ;  *cl_adjustedpp = NULL

   ;  clmCastActors
      (&mx, &cl_adj, &el_to_cl, &el_on_cl, &cl_on_cl, &cl_on_el, 0.95)
   ;  mclxFree(&cl_on_el)

   ;  for (d=0;d<N_COLS(cl_on_cl);d++)
      {  mclv* clthis   =  cl_adj->cols+d
      ;  mclv* cllist   =  cl_on_cl->cols+d
      ;  mclp* pself    =  mclvGetIvp(cllist, clthis->vid, NULL)
      ;  double self_val = -1.0
      
      ;  if (pself)
            self_val = pself->val
         ,  pself->val *= 1.001  /* to push it up in case of equal weights */

;if(0)fprintf(stderr, "test size %d\n", (int) clthis->n_ivps)
      ;  if (prune_sz && clthis->n_ivps > prune_sz)
         continue

      ;  while (1)
         {  mclv* clthat
         ;  dim e
         ;  if (cllist->n_ivps < 2)
            break
         ;  mclvSort(cllist, mclpValRevCmp)

                     /* now get biggest mass provided that cluster
                      * ranks higher (has at least as many entries)
                      *
                      * fixme/todo: we probably have a slight order
                      * dependency for some fringe cases. If provable
                      * then either solve or document it.
                     */
         ;  for (e=0;e<cllist->n_ivps;e++)
            if (cllist->ivps[e].idx >= clthis->vid)
            break

                     /* found none or itself */
         ;  if (e == cllist->n_ivps || cllist->ivps[e].idx == clthis->vid)
            break

         ;  if       /* Should Not Happen */
            (!(clthat
            =  mclxGetVector(cl_adj, cllist->ivps[e].idx, RETURN_ON_FAIL, NULL)
            ) )
            break

                     /*    works for special case prune_sz == 0               */
                     /*    if (clthat->n_ivps + clthis->n_ivps > prune_sz)    */
                     /*    ^iced. inconsistent behaviour as k grows.          */
         ;  {  mcxLog
               (  MCX_LOG_LIST
               ,  me
               ,  "source %ld|%lu|%.3f absorbed by %ld|%lu|%.3f"
               ,  clthis->vid, (ulong) clthis->n_ivps, self_val
               ,  clthat->vid, (ulong) clthat->n_ivps, cllist->ivps[0].val
               )
            ;  n_adjusted += clthis->n_ivps
            ;  (*n_sink)++
                     /* note: we could from our precomputed cl_on_cl
                      * obtain that A is absorbed in B, B is absorbed in C.
                      * below we see that A will be merged with B,
                      * and the result will then be merged with C.
                      * This depends on the fact that cl_adj is ordered
                      * on increasing cluster size.
                     */
            ;  mcldMerge(cl_adj->cols+d, clthat, clthat)
            ;  mclvResize(cl_adj->cols+d, 0)
            ;  mclvInsertIdx(cid_affected, clthat->vid, 2.0)
         ;  }
            break
      ;  }
         mclvSort(cllist, mclpIdxCmp)
   ;  }

      mclxFree(&cl_on_cl)
   ;  mclxFree(&el_on_cl)
   ;  mclxFree(&el_to_cl)

   ;  mclxMakeCharacteristic(cl)

   ;  mclvUnary(cid_affected, fltxGT, &bar_affected)
   ;  *n_source = cid_affected->n_ivps
   ;  mclvFree(&cid_affected)

   ;  mclxColumnsRealign(cl_adj, mclvSizeRevCmp)

   ;  if (!n_adjusted)
      {  mclxFree(&cl_adj)
      ;  return 0
   ;  }

      mclxUnary(cl_adj, fltxCopy, NULL)
   ;  mclxMakeCharacteristic(cl_adj)   

   ;  *cl_adjustedpp  =  cl_adj
   ;  return n_adjusted
;  }
예제 #12
0
static dim clm_clm_adjust
(  mclx* mx
,  mclx* cl
,  dim cls_size_max
,  mclx** cl_adjustedpp
,  mclv** cid_affectedpp
,  mclv** nid_affectedpp
)
   {  dim i, j, n_adjusted = 0
   ;  mclx* cl_adj = mclxCopy(cl)

   ;  mclv* cid_affected = mclvClone(cl->dom_cols)
   ;  mclv* nid_affected = mclvClone(mx->dom_cols)
   ;  double bar_affected = 1.5

   ;  const char* e1 = getenv("MCL_ADJ_FMAX")
   ;  const char* e2 = getenv("MCL_ADJ_EMASS")
   
   ;  double f1 = e1 ? atof(e1) : 2
   ;  double f2 = e2 ? atof(e2) : 3

   ;  mcxbool loggit = mcxLogGet( MCX_LOG_CELL | MCX_LOG_INFO )

   ;  clmVScore sc

   ;  mclx *el_to_cl = NULL
   ;  mclx *el_on_cl = NULL
   ;  mclx *cl_on_cl = NULL
   ;  mclx *cl_on_el = NULL

   ;  *cl_adjustedpp = NULL
   ;  *cid_affectedpp = NULL
   ;  *nid_affectedpp = NULL

   ;  clmCastActors
      (&mx, &cl, &el_to_cl, &el_on_cl, &cl_on_cl, &cl_on_el, 0.95)

   ;  mclxFree(&cl_on_cl)
   ;  mclxFree(&cl_on_el)

   ;  mclvMakeConstant(cid_affected, 1.0)
   ;  mclvMakeConstant(nid_affected, 1.0)


   ;  for (i=0;i<N_COLS(cl_adj);i++)
      cl_adj->cols[i].val = 0.5

                     /*    Proceed with smallest clusters first.
                      *    Caller has to take care of mclxColumnsRealign
                     */
   ;  for (i=0;i<N_COLS(cl);i++)
      {  mclv* clself = cl->cols+i

                     /*    Only consider nodes in clusters of
                      *    size <= cls_size_max
                     */
      ;  if (cls_size_max && clself->n_ivps > cls_size_max)
         break
                     /*    Clusters that have been marked for inclusion
                      *    cannot play.
                     */
      ;  if (cl_adj->cols[i].val > 1)
         continue

      ;  for (j=0;j<clself->n_ivps;j++)
         {  long nid  = clself->ivps[j].idx
         ;  long nos  = mclvGetIvpOffset(mx->dom_cols, nid, -1)
         ;  mclv* clidvec  =  mclxGetVector(el_on_cl, nid, RETURN_ON_FAIL, NULL)

         ;  double eff_alien_bsf = 0.0, eff_alien_max_bsf = 0.0 /* best so far*/
         ;  double eff_self = 0.0, eff_self_max = 0.0
         ;  long cid_alien  = -1, cid_self = -1
         ;  clmVScore sc_self = { 0 }, sc_alien = { 0 }
         ;  dim f

         ;  if (nos < 0 || !clidvec)
            {  mcxErr
               ("clmDumpNodeScores panic", "node <%ld> does not belong", nid)
            ;  continue
         ;  }

            clmVScanDomain(mx->cols+nos, clself, &sc)
         ;  clmVScoreCoverage(&sc, &eff_self, &eff_self_max)
         ;  cid_self = clself->vid
         ;  sc_self  = sc

         ;  if (loggit)
            mcxLog2
            (  us
            ,  "node %ld in cluster %ld eff %.3f,%.3f sum %.3f"
            ,  nid
            ,  cid_self
            ,  eff_self
            ,  eff_self_max
            ,  sc.sum_i
            )

         ;  for (f=0;f<clidvec->n_ivps;f++)
            {  long cid = clidvec->ivps[f].idx
            ;  mclv* clvec = mclxGetVector(cl, cid, RETURN_ON_FAIL, NULL)
                          /* ^ overdoing: cid == clvec->vid */
            ;  double eff, eff_max
            ;  if (!clvec)
               {  mcxErr
                  (  "clmAdjust panic"
                  ,  "cluster <%ld> node <%ld> mishap"
                  ,  cid
                  ,  nid
                  )
               ;  continue
            ;  }


                        /* fixme: document or remove first condition
                         *
                        */
               if ((0 && clvec->n_ivps <= clself->n_ivps) || clvec->vid == cid_self)
               continue

            ;  clmVScanDomain(mx->cols+nos, clvec, &sc)
            ;  clmVScoreCoverage(&sc, &eff, &eff_max)

#if 0
#  define PIVOT eff > eff_alien_bsf
#else
#  define PIVOT eff_max > eff_alien_max_bsf
#endif

            ;  if
               (  PIVOT
               || sc.sum_i >= 0.5
               )
                  eff_alien_bsf = eff
               ,  eff_alien_max_bsf = eff_max
               ,  cid_alien = clvec->vid
               ,  sc_alien = sc

            ;  if (sc.sum_i >= 0.5)
               break
         ;  }

            if (loggit)
            mcxLog2
            (  us
            ,  " -> best alien %ld eff %.3f,%.3f sum %.3f"
            ,  cid_alien
            ,  eff_alien_bsf
            ,  eff_alien_max_bsf
            ,  sc_alien.sum_i
            )

                  /* below: use sum_i as mass fraction
                   * (clmAdjust framework uses stochastic * matrix)
                  */
         ;  if
            (  cid_alien >= 0
            && cid_self >= 0
            && f1 * sc_alien.max_i >= sc_self.max_i
            && (  (  eff_alien_bsf > eff_self
                  && sc_alien.sum_i > sc_self.sum_i
                  )
               || (  pow(sc_alien.sum_i, f2) >= sc_self.sum_i
                  && pow(eff_self, f2) <= eff_alien_bsf
                  )
               )
                  /* So, if max is reasonable
                   * and efficiency is better and mass is better
                   * or if mass is ridiculously better -> move
                   * Somewhat intricate and contrived, yes.
                  */
            )
            {  mclv* acceptor
               =  mclxGetVector(cl_adj, cid_alien, RETURN_ON_FAIL, NULL)
            ;  mclv* donor
               =  mclxGetVector(cl_adj, cid_self,  RETURN_ON_FAIL, NULL)
            ;  if (!donor || !acceptor || acceptor == donor)
               continue

            ;  mclvInsertIdx(donor, nid, 0.0)
            ;  mclvInsertIdx(acceptor, nid, 1.0)
            ;  acceptor->val = 1.5

            ;  if (mcxLogGet(MCX_LOG_LIST))
               {  mclv* nb = mx->cols+nos
               ;  double mxv = mclvMaxValue(nb)
               ;  double avg = nb->n_ivps ? mclvSum(nb) / nb->n_ivps : -1.0
               ;  mcxLog
                  (  MCX_LOG_LIST
                  ,  us
                  ,  "mov %ld (%ld %.2f %.2f)"
                     " %ld (cv=%.2f cm=%.2f s=%.2f m=%.2f #=%lu)"
                     " to %ld (cv=%.2f cm=%.2f s=%.2f m=%.2f #=%lu)"
                  ,  nid
                  ,     (long) nb->n_ivps, mxv, avg
                  ,  cid_self
                  ,     eff_self, eff_self_max, sc_self.sum_i, sc_self.max_i
                  ,              (ulong) (sc_self.n_meet + sc_self.n_ddif)
                  ,  cid_alien
                  ,     eff_alien_bsf, eff_alien_max_bsf, sc_alien.sum_i, sc_alien.max_i
                  ,              (ulong) (sc_alien.n_meet + sc_alien.n_ddif)
                  )
            ;  }

               n_adjusted++                  
            ;  mclvInsertIdx(cid_affected, cid_alien, 2.0)
            ;  mclvInsertIdx(cid_affected, cid_self, 2.0)
            ;  mclvInsertIdx(nid_affected, nid, 2.0)
         ;  }
         }
      }
      mclxFree(&el_on_cl)
   ;  mclxFree(&el_to_cl)

   ;  for (i=0;i<N_COLS(cl_adj);i++)
      cl_adj->cols[i].val = 0.0

   ;  mclxMakeCharacteristic(cl)

   ;  if (!n_adjusted)
      {  mclxFree(&cl_adj)
      ;  mclvFree(&cid_affected)
      ;  mclvFree(&nid_affected)
      ;  return 0
   ;  }

      mclxUnary(cl_adj, fltxCopy, NULL)
   ;  mclxMakeCharacteristic(cl_adj)   
                     /* FIRST REMOVE ENTRIES set to zero (sssst now .. */
                     /* ...) and THEN make it characteristic again     */

   ;  mclvUnary(cid_affected, fltxGT, &bar_affected)
   ;  mclvUnary(nid_affected, fltxGT, &bar_affected)

   ;  *cl_adjustedpp  =  cl_adj
   ;  *cid_affectedpp =  cid_affected
   ;  *nid_affectedpp =  nid_affected

   ;  return n_adjusted
;  }