Rev 8487 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** PAM := Partitioning Around Medoids** original Id: pam.f,v 1.16 2003/06/03 13:40:56 maechler translated by* f2c (version 20031025) and run through f2c-clean,v 1.10 2002/03/28*/#include <float.h>#include <Rmath.h>#include <Rinternals.h>#include <R_ext/Print.h>/* for diagnostics */#include <R_ext/Utils.h>/* for interrupting */#include "cluster.h"#include "ind_2.h"// carries out a clustering using the k-medoid approachSEXP cl_Pam(SEXP k_, SEXP n_,SEXP do_diss_, /* == !diss; if true, compute distances from x (= x_or_diss);otherwise distances provided by x_or_diss */SEXP x_or_diss,// this "is" if(do_diss) "x[]" (n x p) else "dys[]"SEXP all_stats_, // all_stats == !cluster.onlySEXP medoids, // NULL or integer(k) subset {1:n}SEXP do_swap_, SEXP trace_lev_,SEXP keep_diss_, SEXP pam_once_,// the next 3 are only needed if(do_diss)SEXP val_md, SEXP j_md, // "md" := [m]issing [d]ataSEXP diss_kind_) // = 1 ("euclidean") or 2 ("manhattan"){const int kk = asInteger(k_), n = asInteger(n_),pam_once = asInteger(pam_once_),trace_lev = asInteger(trace_lev_);const Rboolean all_stats = asLogical(all_stats_), med_given = (medoids != R_NilValue) // if true, med[] contain initial medoids, do_diss = asLogical(do_diss_), do_swap = asLogical(do_swap_), keep_diss = asLogical(keep_diss_) // only if(keep_diss) return dys[] .., do_syl = all_stats && (1 < kk && kk < n);#ifdef once_we_get_n_from_argsint n, p = NA_INTEGER;if (do_diss) { // <-- was 'jdyss != 1' i.e. jdyss == 0SEXP dims = getAttrib(x_or_diss, R_DimSymbol);n = INTEGER(dims)[0];p = INTEGER(dims)[1];} else {n = asInteger(getAttrib(x_or_diss, install("Size")));}#endifint i, nhalf; // nhalf := #{distances} = length(dys)double s;if (n % 2 == 0) { // avoid overflow of n * (n - 1)nhalf = n / 2 * (n - 1);} else {nhalf = (n - 1) / 2 * n;}int *nsend = (int*) R_alloc(n, sizeof(int)), *nelem = (int*) R_alloc(all_stats ? n : 1, sizeof(int)) /* Rboolean */, *nrepr = (int*) R_alloc(n, sizeof(int)), *med;double*radus = (double*) R_alloc( n, sizeof(double)),*damer = (double*) R_alloc( n, sizeof(double)),*separ = (double*) R_alloc(kk, sizeof(double));int clusinf_dim1 = kk;if(med_given) {if(TYPEOF(medoids) != INTSXP || LENGTH(medoids) != kk)error(_("Invalid 'medoids'"));PROTECT(medoids = duplicate(medoids));} else {PROTECT(medoids = allocVector(INTSXP, kk));}med = INTEGER(medoids);SEXP nms,ans = PROTECT(allocVector(VECSXP, keep_diss ? 9 : 9-1));setAttrib(ans, R_NamesSymbol,nms = allocVector(STRSXP, keep_diss ? 9 : 9-1));int nprot = 2; // <- ++ for each PROTECT() belowSEXP dys_, avsyl_, obj_, clu_, clusinf_, sylinf_, nisol_,ttsyl_;// these are only used if(do_diss) :double *valmd; int *jtmd;int diss_kind = -1;if (do_diss) { // <-- was 'jdyss != 1' i.e. jdyss == 0PROTECT(dys_ = allocVector(REALSXP, nhalf)); nprot++;valmd = REAL(val_md);jtmd = INTEGER(j_md);diss_kind = INTEGER(diss_kind_)[0]; // = 1 ("euclidean") or 2 ("manhattan")} else {dys_ = x_or_diss; // a pointer to the same thing}// Creating the SEXPs as list components, so they are auto-PROTECTed:SET_STRING_ELT(nms, 0, mkChar("clu"));SET_VECTOR_ELT(ans, 0, clu_ = allocVector(INTSXP, n));SET_STRING_ELT(nms, 1, mkChar("med")); SET_VECTOR_ELT(ans, 1, medoids);SET_STRING_ELT(nms, 2, mkChar("silinf"));if(do_syl)SET_VECTOR_ELT(ans, 2, sylinf_ = all_stats ? allocMatrix(REALSXP, n, 4): allocVector(REALSXP, 1));SET_STRING_ELT(nms, 3, mkChar("obj"));SET_VECTOR_ELT(ans, 3, obj_ = allocVector(REALSXP, 2));SET_STRING_ELT(nms, 4, mkChar("isol")); // 'isolation' {0,1,2}: L=1, L*=2 - clustersSET_VECTOR_ELT(ans, 4, nisol_ = allocVector(INTSXP, all_stats ? kk : 1));SET_STRING_ELT(nms, 5, mkChar("clusinf"));SET_VECTOR_ELT(ans, 5, clusinf_ = all_stats ? allocMatrix(REALSXP, kk, 5): allocVector(REALSXP, 1));SET_STRING_ELT(nms, 6, mkChar("avsil"));SET_VECTOR_ELT(ans, 6, avsyl_ = allocVector(REALSXP, n));SET_STRING_ELT(nms, 7, mkChar("ttsil"));if(do_syl)SET_VECTOR_ELT(ans, 7, ttsyl_ = allocVector(REALSXP, 1));if(keep_diss) {SET_STRING_ELT(nms, 8, mkChar("dys")); SET_VECTOR_ELT(ans, 8, dys_);}int *ncluv = INTEGER(clu_),*nisol = INTEGER(nisol_);double*dys = REAL(dys_),*avsyl = REAL(avsyl_),*obj = REAL(obj_),*clusinf= REAL(clusinf_);if (do_diss) { // <-- was 'jdyss != 1' i.e. jdyss == 0double *x = REAL(x_or_diss);SEXP dims = getAttrib(x_or_diss, R_DimSymbol);int p = INTEGER(dims)[1];if(trace_lev)Rprintf("C pam(): computing %d dissimilarities from %d x %d matrix: ",nhalf, n, p);int jhalt = dysta(n, p, x, dys, diss_kind, jtmd, valmd); /* --> ./dysta.c */if (jhalt != 0) {if(trace_lev) Rprintf(" dysta()-error: jhalt=%d\n", jhalt);UNPROTECT(nprot);return ScalarInteger(jhalt); // i.e., integer error code instead of a named list}// elseif(trace_lev) Rprintf("[Ok]\n");}/* s := max( dys[.] ), the largest distance */for (i = 0, s = 0.; i < nhalf; ++i)if (s < dys[i])s = dys[i];/* FIXME: work with med[] = (i_1, i_2, ..., i_k)* ----- instead nrepr[] = (b_1, ... b_n) b_i in {0,1} */for (i = 0; i < n; ++i)nrepr[i] = 0;if(med_given) { /* if true, med[] contain initial medoids *//* for the moment, translate these to nrepr[] 0/1 :* not assuming that the med[] indices are sorted */for (int k = 0; k < kk; k++)nrepr[med[k] - 1] = 1;}/* Build + Swap [but no build if(med_given); swap only if(do_swap) : */bswap(kk, n, nrepr, // <- 3med_given, do_swap, trace_lev,// <- 6radus, damer, avsyl, // <- 9dys, s, obj, // <- 12pam_once);if(trace_lev) Rprintf("end{bswap()}, ");/* Compute Clustering & STATs if(all_stats): */cstat(kk, n, nsend, nrepr, all_stats,radus, damer, avsyl, separ, &s, dys, ncluv, nelem, med, nisol);if(trace_lev) Rprintf("end{cstat()}\n");if(all_stats) {for (int k = 0; k < kk; ++k) {clusinf[k]= (double) nrepr[k];clusinf[k + clusinf_dim1] = radus[k];clusinf[k + (clusinf_dim1 << 1)] = avsyl[k];clusinf[k + clusinf_dim1 * 3] = damer[k];clusinf[k + (clusinf_dim1 << 2)] = separ[k];}if (do_syl) { // Compute Silhouette info :double*ttsyl = REAL(ttsyl_),*sylinf = REAL(sylinf_);dark(kk, n, ncluv, dys, s,// -->nsend, nelem, nrepr, radus, damer, avsyl, ttsyl, sylinf);}}UNPROTECT(nprot);return ans;} /* cl_Pam *//* -----------------------------------------------------------bswap(): the clustering algorithm in 2 parts: I. build, II. swap*/void bswap(int kk, int n, int *nrepr,Rboolean med_given, Rboolean do_swap, int trace_lev,/* nrepr[]: here is boolean (0/1): 1 = "is representative object" */double *dysma, double *dysmb, double *beter,const double dys[], double s, double *obj, int pamonce){int i, j, k,h, dig_n;double sky;/* Parameter adjustments */--nrepr;--beter;--dysma; --dysmb;if(trace_lev) Rprintf("pam()'s bswap(*, s=%g, pamonce=%d): ", s, pamonce);s = s * 1.1 + 1.;// larger than all dys[] (but DBL_MAX is too large)/* IDEA: when n is large compared to k (= kk),* ---- rather use a "sparse" representation:* instead of boolean vector nrepr[] , use ind_repr <- which(nrepr) !!*/for (i = 1; i <= n; ++i)dysma[i] = s;if(med_given) {if(trace_lev) Rprintf("medoids given; ");/* compute dysma[] : dysma[j] = D(j, nearest_representative) */for (i = 1; i <= n; ++i) {if (nrepr[i] == 1)for (j = 1; j <= n; ++j) {double dij = dys_2(dys, i, j);if (dysma[j] > dij)dysma[j] = dij;}}}else { // ====== first algorithm: BUILD. ======================if(trace_lev) Rprintf("build %d medoids:\n", kk);/* find kk representatives aka medoids : */for (k = 1; k <= kk; ++k) {R_CheckUserInterrupt();/* compute beter[i] for all non-representatives:* also find ammax := max_{..} and nmax := argmax_i{beter[i]} ... */int nmax = -1; /* -Wall */double ammax, cmd;ammax = 0.;for (i = 1; i <= n; ++i) {if (nrepr[i] == 0) {beter[i] = 0.;for (j = 1; j <= n; ++j) {cmd = dysma[j] - dys_2(dys, i, j);if (cmd > 0.)beter[i] += cmd;}if (ammax <= beter[i]) {/* does < (instead of <= ) work too? -- NO! */ammax = beter[i];nmax = i;}}}nrepr[nmax] = 1;/* = .true. : found new representative */if (trace_lev >= 2)Rprintf(" new repr. %d\n", nmax);/* update dysma[] : dysma[j] = D(j, nearest_representative) */for (j = 1; j <= n; ++j) {double dij = dys_2(dys, nmax, j);if (dysma[j] > dij)dysma[j] = dij;}}/* output of the above loop: nrepr[], dysma[], ... */}if(trace_lev) /* >= 2 (?) */ {dig_n = 1+(int) log10(n); // = 1 + floor(log10(n))Rprintf(" after build: medoids are");for (i = 1; i <= n; ++i)if(nrepr[i] == 1) Rprintf(" %*d", dig_n, i);if(trace_lev >= 3) {Rprintf("\n and min.dist dysma[1:n] are\n");for (i = 1; i <= n; ++i) {Rprintf(" %6.3g", dysma[i]);if(i % 10 == 0) Rprintf("\n");}if(n % 10 != 0) Rprintf("\n");} else Rprintf("\n");} else dig_n = 1;// -Wallsky = 0.;for (j = 1; j <= n; ++j)sky += dysma[j];obj[0] = sky / n;if (do_swap && (kk > 1 || med_given)) {double dzsky;int hbest = -1, nbest = -1, kbest= -1; // -Wallint *medoids, *clustmembership, *clustmembershipb;double *fvect;if(pamonce) {// +1 --> use 1-based indices (as R)medoids = (int*) R_alloc(kk+1, sizeof(int));clustmembership = (int*) R_alloc(n+1, sizeof(int));fvect = (double*) R_alloc(n+1, sizeof(double));for (int k = 1, i = 1; i <= n; ++i) {if (nrepr[i]) {medoids[k] = i;k++;}}} else { // -Wall :clustmembership = medoids = (int*) NULL;clustmembershipb = medoids = (int*) NULL;fvect = (double*) NULL;}int *best_h = NULL; double *best_d = NULL;if (pamonce == 4 || pamonce == 5) { // Schubert and Rousseeuw 2019 FastPAM2best_h = (int*) R_alloc(kk+1, sizeof(int));best_d = (double*) R_alloc(kk+1, sizeof(double));}// Improvement part of Schubert and Rousseeuw 2021 FasterPAMdouble *remove_loss = NULL;if (pamonce >= 3 && pamonce <= 6) {remove_loss = (double*) R_alloc(kk+1, sizeof(double));clustmembershipb = (int*) R_alloc(n+1, sizeof(int));}/* ====== second algorithm: SWAP. ====== *//* Hmm: In the following, we RE-compute dysma[];* don't need it first time; then only need *update* after swap */// Used by FasterPAM only (Schubert and Rousseeuw 2021, pamonce = 6)int lastswap = 0;/*-- Loop : */L60:if(pamonce == 0) { // original algorithmfor (j = 1; j <= n; ++j) {/* dysma[j] := D_j d(j, <closest medi>) [KR p.102, 104]* dysmb[j] := E_j d(j, <2-nd cl.medi>) [p.103] */dysma[j] = s;dysmb[j] = s;for (i = 1; i <= n; ++i) {if (nrepr[i]) {double dij = dys_2(dys, i, j);if (dysma[j] > dij) {dysmb[j] = dysma[j];dysma[j] = dij;} else if (dysmb[j] > dij) {dysmb[j] = dij;}}}}} else if (pamonce >= 3 && pamonce <= 6) { // Schubert and Rousseeuw 2019 FastPAM1/2for (j = 1; j <= n; ++j) {/* dysma[j] := D_j d(j, <closest medi>) [KR p.102, 104]* dysmb[j] := E_j d(j, <2-nd cl.medi>) [p.103] */double dysmaj = s, dysmbj = s;for(k = 1; k <= kk; k++) {double dysij = dys_2(dys, medoids[k], j);if (dysmaj > dysij) {clustmembershipb[j] = clustmembership[j];dysmbj = dysmaj;clustmembership[j] = k; // Use medoid k, not idysmaj = dysij;} else if (dysmbj > dysij) {clustmembershipb[j] = k;dysmbj = dysij;}}dysma[j] = dysmaj;dysmb[j] = dysmbj;}} else { // pamonce == 1 or == 2 :for (j = 1; j <= n; ++j) {/* dysma[j] := D_j d(j, <closest medi>) [KR p.102, 104]* dysmb[j] := E_j d(j, <2-nd cl.medi>) [p.103] */dysma[j] = s;dysmb[j] = s;for(k = 1; k <= kk; k++) {i = medoids[k];double dij = dys_2(dys, i, j);if (dysma[j] > dij) {//store cluster membershipclustmembership[j] = i;dysmb[j] = dysma[j];dysma[j] = dij;} else if (dysmb[j] > dij) {dysmb[j] = dij;}}}}// FasterPAM improvement: do not recompute the nearest, but update it in the end as necessaryL60b:dzsky = 1.; /* 1 is arbitrary > 0; only dzsky < 0 matters in the end */if(pamonce == 0) { // original algorithmfor (h = 1; h <= n; ++h) if (!nrepr[h]) {R_CheckUserInterrupt();for (i = 1; i <= n; ++i) if (nrepr[i]) {double dz = 0.;/* dz := T_{ih} := sum_j C_{jih} [p.104] : */for (j = 1; j <= n; ++j) { /* if (!nrepr[j]) { */double dhj = dys_2(dys, h, j);double dij = dys_2(dys, i, j);if (dij == dysma[j]) {double small = dysmb[j] > dhj ? dhj : dysmb[j];dz += (- dysma[j] + small);} else if (dhj < dysma[j]) /* 1c. */dz += (- dysma[j] + dhj);}if (dzsky > dz) {dzsky = dz; /* dzsky := min_{i,h} T_{i,h} */hbest = h;nbest = i;}}}} else if (pamonce == 1 || pamonce == 2) {for(k = 1; k <= kk; k++) {R_CheckUserInterrupt();i=medoids[k];double removeCost = 0.;//Compute cost for removing the medoidfor (j = 1; j <= n; ++j) {if(clustmembership[j] == i) {removeCost+=(dysmb[j]-dysma[j]);fvect[j]=dysmb[j];}else{fvect[j]=dysma[j];}}if (pamonce == 1) {// Now check possible new medoids hfor (h = 1; h <= n; ++h) if (!nrepr[h]) {double addGain = removeCost;// Compute gain of adding h as a medoid:for (j = 1; j <= n; ++j) {double dhj = dys_2(dys, h, j);if(dhj < fvect[j])addGain += (dhj-fvect[j]);}if (dzsky > addGain) {dzsky = addGain; /* dzsky := min_{i,h} T_{i,h} */hbest = h;nbest = i;kbest = k;}}} else { // pamonce == 2 :// Now check possible new medoids hfor (h = 1; h <= n; ++h) if (!nrepr[h]) {double addGain = removeCost - fvect[h]; // - fvect[h] since dys[h,h]=0;// Compute gain of adding h as a medoid:int ijbase = (h-2)*(h-1)/2;for (j = 1; j < h; ++j) { // ijbase+j >= 0 + j >= 1double dhj = dys[ijbase+j-1];if(dhj < fvect[j])addGain += (dhj-fvect[j]);}ijbase += h;// = (h-2)*(h-1)/2 + h >= 1for (j = h+1; j <= n; ++j) {ijbase += j-2; // >= 1 (as j-2 >= 0)if(dys[ijbase-1] < fvect[j])addGain += (dys[ijbase-1]-fvect[j]);}if (dzsky > addGain) {dzsky = addGain; /* dzsky := min_{i,h} T_{i,h} */hbest = h;nbest = i;kbest = k;}}}}} else if (pamonce >= 3 && pamonce <= 6) { // Schubert and Rousseeuw variants// cost of removing current medoidsfor(k = 1; k <= kk; k++)remove_loss[k] = 0;for (j = 1; j <= n; ++j)remove_loss[clustmembership[j]] += dysmb[j] - dysma[j];if (pamonce == 3) { // Schubert and Rousseeuw 2019 FastPAM1 -- improved 2021 version// Now check possible new medoids hfor (h = 1; h <= n; ++h) if (!nrepr[h]) {R_CheckUserInterrupt();for(k = 1; k <= kk; k++)beter[k] = 0;double acc = 0;// Compute gain of substituting h for each other medoid:for (j = 1; j <= n; ++j) {double dist_h = dys_2(dys, h, j); // New medoidint memb = clustmembership[j]; // Medoid nr of nearestdouble distcur = dysma[j]; // Nearestdouble distsec = dysmb[j]; // Second nearest// New medoid is new best:if (dist_h < distcur) {acc += dist_h - distcur;beter[memb] += distcur - distsec;} else if (dist_h < distsec) {beter[memb] += dist_h - distsec;}}for(k = 1; k <= kk; k++) {double loss = beter[k] + acc + remove_loss[k];if (loss < dzsky) {dzsky = loss;hbest = h;nbest = medoids[k];kbest = k;}}}} else if (pamonce == 4) { // Schubert and Rousseeuw 2019 FastPAM2for(k = 1; k <= kk; k++) {best_d[k] = 1.; // arbitrary > 0}// Now check possible new medoids hfor (h = 1; h <= n; ++h) if (!nrepr[h]) {R_CheckUserInterrupt();for(k = 1; k <= kk; k++)beter[k] = 0;// Compute gain of substituting h for each other medoid:double acc = 0;for (j = 1; j <= n; ++j) {double dist_h = dys_2(dys, h, j); // New medoidint memb = clustmembership[j]; // Medoid nr of nearestdouble distcur = dysma[j]; // Nearestdouble distsec = dysmb[j]; // Second nearest// New medoid is new best:if (dist_h < distcur) {acc += dist_h - distcur;beter[memb] += distcur - distsec;} else if (dist_h < distsec) {beter[memb] += dist_h - distsec;}}for(k = 1; k <= kk; k++) {double loss = beter[k] + acc + remove_loss[k];if (loss < best_d[k]) {best_d[k] = loss;best_h[k] = h;}}}// Pick one best medoid at the end (additional swaps come later).for(k = 1; k <= kk; k++) {if (best_d[k] < dzsky) {dzsky = best_d[k];kbest = k;}}hbest = best_h[kbest];nbest = medoids[kbest];} else if (pamonce == 5) { // Schubert and Rousseeuw 2019 FastPAM2,// with linearized memory access as in Reynolds 2for(k = 1; k <= kk; k++) {best_d[k] = 1.; // arbitrary > 0}// Now check possible new medoids hfor (h = 1; h <= n; ++h) if (!nrepr[h]) {R_CheckUserInterrupt();for(k = 1; k <= kk; k++)beter[k] = 0;double acc = -dysma[h];beter[clustmembership[h]] = dysma[h] - dysmb[h];// Compute gain of substituting h for each other medoid:int ijbase = (h-2)*(h-1)/2;for (j = 1; j < h; ++j) {double dist_h = dys[ijbase+j-1]; // New medoidint memb = clustmembership[j]; // Medoid nr of nearestdouble distcur = dysma[j]; // Nearestdouble distsec = dysmb[j]; // Second nearest// New medoid is new best:if (dist_h < distcur) {acc += dist_h - distcur;beter[memb] += distcur - distsec;} else if (dist_h < distsec) {beter[memb] += dist_h - distsec;}}ijbase += h;// = (h-2)*(h-1)/2 + hfor (j = h+1; j <= n; ++j) {ijbase += j-2; // (j-2) >= 0double dist_h = dys[ijbase-1]; // New medoidint memb = clustmembership[j]; // Medoid nr of nearestdouble distcur = dysma[j]; // Nearestdouble distsec = dysmb[j]; // Second nearest// New medoid is new best:if (dist_h < distcur) {acc += dist_h - distcur;beter[memb] += distcur - distsec;} else if (dist_h < distsec) {beter[memb] += dist_h - distsec;}}for(k = 1; k <= kk; k++) {double loss = beter[k] + acc + remove_loss[k];if (loss < best_d[k]) {best_d[k] = loss;best_h[k] = h;}}}// Pick one best medoid at the end (additional swaps come later).for(k = 1; k <= kk; k++) {if (best_d[k] < dzsky) {dzsky = best_d[k];kbest = k;}}hbest = best_h[kbest];nbest = medoids[kbest];} else { // Schubert and Rousseeuw, 2021, FasterPAM// Check possible new medoids h// Beginning at the last swap performedint h2 = 1;for (h = lastswap + 1; h2 <= n; ++h, ++h2) {if (h == n+1) h = 1; // wrap aroundif (nrepr[h]) continue;R_CheckUserInterrupt();for(k = 1; k <= kk; k++)beter[k] = 0;double acc = -dysma[h];beter[clustmembership[h]] += dysma[h] - dysmb[h];// Compute gain of substituting h for each other medoid:int ijbase = (h-2)*(h-1)/2;for (j = 1; j < h; ++j) {double dist_h = dys[ijbase+j-1]; // New medoidint memb = clustmembership[j]; // Medoid nr of nearestdouble distcur = dysma[j]; // Nearestdouble distsec = dysmb[j]; // Second nearest// New medoid is new best:if (dist_h < distcur) {acc += dist_h - distcur;beter[memb] += distcur - distsec;} else if (dist_h < distsec) {beter[memb] += dist_h - distsec;}}ijbase += h;// = (h-2)*(h-1)/2 + hfor (j = h+1; j <= n; ++j) {ijbase += j-2;double dist_h = dys[ijbase-1]; // New medoidint memb = clustmembership[j]; // Medoid nr of nearestdouble distcur = dysma[j]; // Nearestdouble distsec = dysmb[j]; // Second nearest// New medoid is new best:if (dist_h < distcur) {acc += dist_h - distcur;beter[memb] += distcur - distsec;} else if (dist_h < distsec) {beter[memb] += dist_h - distsec;}}for(k = 1; k <= kk; k++) {double loss = beter[k] + acc + remove_loss[k];if (loss < dzsky) {dzsky = loss;hbest = h;nbest = medoids[k];kbest = k;}}// Execute the first swap found.if (dzsky < 0) {break;}}}} else {Rprintf("Invalid pamonce value: %d\n", pamonce);return;}if (dzsky < - 16*DBL_EPSILON * fabs(sky)) { // basically " < 0 ",// but ' < 0 ' gave infinite loop, swapping the identical objects// found an improving swapif(trace_lev >= 2)Rprintf( " swp new %*d <-> %*d old; decreasing diss. %7g by %g\n",dig_n, hbest, dig_n, nbest, sky, dzsky);nrepr[hbest] = 1;nrepr[nbest] = 0;if(pamonce)medoids[kbest]=hbest;if(pamonce == 6)lastswap = hbest;sky += dzsky;if (pamonce == 3 || pamonce == 6) { // FasterPAM improvement, Schubert and Rousseeuw 2021// Update dysma, dysmb rather than recomputing itdouble newloss = 0;for (j = 1; j <= n; ++j) {/* dysma[j] := D_j d(j, <closest medi>) [KR p.102, 104]* dysmb[j] := E_j d(j, <2-nd cl.medi>) [p.103] */double dnew = dys_2(dys, hbest, j);if (clustmembership[j] == kbest) {if (dnew < dysmb[j]) {// Clustmembership already kbestdysma[j] = dnew;} else {double dysmaj = s, dysmbj = s;for(k = 1; k <= kk; k++) {double dysij = dys_2(dys, medoids[k], j);if (dysmaj > dysij) {clustmembershipb[j] = clustmembership[j];dysmbj = dysmaj;//store cluster membershipclustmembership[j] = k; // Use medoid k, not idysmaj = dysij;} else if (dysmbj > dysij) {clustmembershipb[j] = k; // Use medoid k, not idysmbj = dysij;}}dysma[j] = dysmaj;dysmb[j] = dysmbj;}} else {if (dnew < dysma[j]) {clustmembershipb[j] = clustmembership[j]; // not kbestdysmb[j] = dysma[j];clustmembership[j] = kbest; // Use medoid k, not idysma[j] = dnew;} else if (clustmembershipb[j] == kbest) {double dysmaj = s, dysmbj = s;for(k = 1; k <= kk; k++) {double dysij = dys_2(dys, medoids[k], j);if (dysmaj > dysij) {clustmembershipb[j] = clustmembership[j];dysmbj = dysmaj;//store cluster membershipclustmembership[j] = k; // Use medoid k, not idysmaj = dysij;} else if (dysmbj > dysij) {clustmembershipb[j] = k; // Use medoid k, not idysmbj = dysij;}}dysma[j] = dysmaj;dysmb[j] = dysmbj;} else if (dnew < dysmb[j]) {clustmembershipb[j] = kbest; // Use medoid k, not idysmb[j] = dnew;}}newloss += dysma[j];}sky = newloss; // more accurate than accumulating changesgoto L60b;}// FastPAM2, Schubert and Rousseeuw 2019if (pamonce == 4 || pamonce == 5)do {best_d[kbest] = 1; // Deactivate// Find next best:dzsky = 1;for(k = 1; k <= kk; k++) {if (best_d[k] < dzsky) {dzsky = best_d[k];kbest = k;}}hbest = best_h[kbest];nbest = medoids[kbest];if(dzsky >= - 16*DBL_EPSILON * fabs(sky))break;// else try more :// FIXME: duplicated code from above - update stats.for (j = 1; j <= n; ++j) {/* dysma[j] := D_j d(j, <closest medi>) [KR p.102, 104]* dysmb[j] := E_j d(j, <2-nd cl.medi>) [p.103] */dysma[j] = s;dysmb[j] = s;for(k = 1; k <= kk; k++) {i = medoids[k];double dij = dys_2(dys, i, j);if (dysma[j] > dij) {//store cluster membershipclustmembership[j] = k; // Use medoid k, not idysmb[j] = dysma[j];dysma[j] = dij;} else if (dysmb[j] > dij) {dysmb[j] = dij;}}}// Recompute dzsky value, again.dzsky = 0;for (j = 1; j <= n; ++j) { /* if (!nrepr[j]) { */double dhj = dys_2(dys, hbest, j),dij = dys_2(dys, nbest, j);if (dij == dysma[j]) {dzsky += (- dysma[j] + fmin2(dysmb[j], dhj) );} else if (dhj < dysma[j]) /* 1c. */dzsky += (- dysma[j] + dhj);}if(dzsky >= - 16*DBL_EPSILON * fabs(sky))break;// else try more :if(trace_lev >= 2)Rprintf( " fswp new %*d <-> %*d old; decreasing diss. %7g by %g\n",dig_n, hbest, dig_n, nbest, sky, dzsky);nrepr[hbest] = 1;nrepr[nbest] = 0;medoids[kbest]=hbest;sky += dzsky;} while(1); // (pamonce = 4 or 5)goto L60;}}obj[1] = sky / n;} /* bswap *//* -----------------------------------------------------------cstat(): Compute STATistics (numerical output) concerning each partition*/void cstat(int kk, int nn, int *nsend, int *nrepr, Rboolean all_stats,double *radus, double *damer, double *avsyl, double *separ, double *s,double *dys, int *ncluv, int *nelem, int *med, int *nisol){int j, k, ja, jk, nplac, ksmal = -1/* -Wall */;double ss = *s * 1.1 + 1.;/* Parameter adjustments */--ncluv;--nrepr;--nsend;/* nsend[j] := i, where x[i,] is the medoid to which x[j,] belongs */for (j = 1; j <= nn; ++j) {if (nrepr[j] == 0) {double dsmal = ss;for (k = 1; k <= nn; ++k) {if (nrepr[k] == 1) {double dkj = dys_2(dys, k, j);if (dsmal > dkj) {dsmal = dkj;ksmal = k;}}}nsend[j] = ksmal;} else {nsend[j] = j;}}/* ncluv[j] := k , the cluster number (k = 1..kk) */jk = 1;nplac = nsend[1];for (j = 1; j <= nn; ++j) {ncluv[j] = 0;if (nsend[j] == nplac)ncluv[j] = 1;}for (ja = 2; ja <= nn; ++ja) {nplac = nsend[ja];if (ncluv[nplac] == 0) {++jk;for (j = 2; j <= nn; ++j) {if (nsend[j] == nplac)ncluv[j] = jk;}if (jk == kk)break;}}if(all_stats) { /* analysis of the clustering. *//* int numl; */--avsyl; // <-> [1]-indexing--damer;--med;--nelem;--nisol;--radus;--separ;for (k = 1; k <= kk; ++k) {int ntt = 0, m = -1/* -Wall */;double ttt = 0.;radus[k] = -1.;R_CheckUserInterrupt();for (j = 1; j <= nn; ++j) {if (ncluv[j] == k) {double djm;++ntt;m = nsend[j];nelem[ntt] = j;djm = dys_2(dys, j, m);ttt += djm;if (radus[k] < djm)radus[k] = djm;}}if(ntt == 0) error(_("pam(): Bug in C level cstat(), k=%d: ntt=0"), k);avsyl[k] = ttt / ntt;med[k] = m;}if (kk == 1) {damer[1] = *s;nrepr[1] = nn;nisol[1] = 0;separ[1] = 0.;return;}/* ELSE kk > 1 : *//* numl = number of L-clusters. *//* numl = 0; */for (k = 1; k <= kk; ++k) {/*identification of cluster k:nelem= vector of object indices,nel = number of objects*/int nel = 0;R_CheckUserInterrupt();for (j = 1; j <= nn; ++j) {if (ncluv[j] == k) {++nel;nelem[nel] = j;}}nrepr[k] = nel;if (nel == 1) {int nvn = nelem[1];damer[k] = 0.;separ[k] = ss;for (j = 1; j <= nn; ++j) {if (j != nvn) {int mevj = ind_2(nvn, j);if (separ[k] > dys[mevj])separ[k] = dys[mevj];}}/* Is cluster k1) an L-cluster or2) an L*-cluster ? *//* if (separ[k] == 0.) *//* ++numl; */}else { /* nel != 1 : */double dam = -1., sep = ss;Rboolean kand = TRUE;for (ja = 1; ja <= nel; ++ja) {int jb, nvna = nelem[ja];double aja = -1., ajb = ss;for (jb = 1; jb <= nn; ++jb) {double djndz = dys_2(dys, nvna, jb);if (ncluv[jb] == k) {if (aja < djndz)aja = djndz;} else {if (ajb > djndz)ajb = djndz;}}if (kand && aja >= ajb)kand = FALSE;if (dam < aja)dam = aja;if (sep > ajb)sep = ajb;}separ[k] = sep;damer[k] = dam;if (kand) {/* ++numl; */if (dam >= sep) /* L-cluster */nisol[k] = 1;else/* L*-cluster */nisol[k] = 2;continue /* k */;}}/* nel = 1 or (!kand) : */nisol[k] = 0;}/* for(k) */} /* all_stats */} /* cstat *//* -----------------------------------------------------------Compute Silhouette Information :*/void dark(// input:int kk, int nn, const int ncluv[], const double dys[], double s,// output:int *nsend, int *nelem, int *negbr,double *syl, double *srank, double *avsyl, double *ttsyl,double *sylinf){int k, nsylr;/* pointers to sylinf[] columns -- sylinf[nn, 4] : */double *sylinf_2, *sylinf_3, *sylinf_4;sylinf_2 = sylinf + nn;sylinf_3 = sylinf_2 + nn;sylinf_4 = sylinf_3 + nn;/* Parameter adjustments */--avsyl;--ncluv;nsylr = 0;*ttsyl = 0.;for (k = 1; k <= kk; ++k) {/* nelem[0:(ntt-1)] := indices (1-based) of obs. in cluster k : */int j,l, ntt = 0;for (j = 1; j <= nn; ++j) {if (ncluv[j] == k) {nelem[ntt] = j;++ntt;}}for (j = 0; j < ntt; ++j) {/* (j+1)-th obs. in cluster k */int k_, nj = nelem[j];double dysb = s * 1.1 + 1.;negbr[j] = -1;/* for all clusters k_ != k : */for (k_ = 1; k_ <= kk; ++k_) if (k_ != k) {double db = 0.;int nbb = 0;for (l = 1; l <= nn; ++l) if (ncluv[l] == k_) {++nbb;if (l != nj)db += dys_2(dys, nj, l);}db /= nbb; /* now db(k_) := mean( d[j, l]; l in C_{k_} ) */if (dysb > db) {dysb = db;negbr[j] = k_;}}/* negbr[j] := arg max_{k_} db(k_) */if (ntt > 1) {double dysa = 0.;for (l = 0; l < ntt; ++l) {int nl = nelem[l];if (nj != nl)dysa += dys_2(dys, nj, nl);}dysa /= ntt - 1;if (dysa > 0.) {if (dysb > 0.) {if (dysb > dysa)syl[j] = 1. - dysa / dysb;else if (dysb < dysa)syl[j] = dysb / dysa - 1.;else /* dysb == dysa: */syl[j] = 0.;if (syl[j] < -1.)syl[j] = -1.;else if (syl[j] > 1.)syl[j] = 1.;} else {syl[j] = -1.;}}else /* dysa == 0 */ if (dysb > 0.)syl[j] = 1.;elsesyl[j] = 0.;}else { /* ntt == 1: */syl[j] = 0.;}} /* for( j ) */avsyl[k] = 0.;if (ntt == 0) /* this can happen when medoids are user-specified !*/continue; /* next k */for (j = 0; j < ntt; ++j) {int lang=-1 /*Wall*/;double symax = -2.;for (l = 0; l < ntt; ++l) {if (symax < syl[l]) {symax = syl[l];lang = l;}}nsend[j] = lang;srank[j] = symax; /* = syl[lang] */avsyl[k] += srank[j];syl[lang] = -3.;}*ttsyl += avsyl[k];avsyl[k] /= ntt;if (ntt == 1) {sylinf [nsylr] = (double) k;sylinf_2[nsylr] = (double) negbr[0];sylinf_3[nsylr] = 0.;sylinf_4[nsylr] = (double) nelem[0];++nsylr;} else {for (j = 0; j < ntt; ++j) {int lplac = nsend[j];sylinf [nsylr] = (double) k;sylinf_2[nsylr] = (double) negbr[lplac];sylinf_3[nsylr] = srank[j];sylinf_4[nsylr] = (double) nelem[lplac];++nsylr;}}} /* for (k) */*ttsyl /= nn;} /* dark */