//#define IGL_LINPROG_VERBOSE IGL_INLINE bool igl::linprog( const Eigen::VectorXd & c, const Eigen::MatrixXd & _A, const Eigen::VectorXd & b, const int k, Eigen::VectorXd & x) { // This is a very literal translation of // http://www.mathworks.com/matlabcentral/fileexchange/2166-introduction-to-linear-algebra/content/strang/linprog.m using namespace Eigen; using namespace std; bool success = true; // number of constraints const int m = _A.rows(); // number of original variables const int n = _A.cols(); // number of iterations int it = 0; // maximum number of iterations //const int MAXIT = 10*m; const int MAXIT = 100*m; // residual tolerance const double tol = 1e-10; const auto & sign = [](const Eigen::VectorXd & B) -> Eigen::VectorXd { Eigen::VectorXd Bsign(B.size()); for(int i = 0;i<B.size();i++) { Bsign(i) = B(i)>0?1:(B(i)<0?-1:0); } return Bsign; }; // initial (inverse) basis matrix VectorXd Dv = sign(sign(b).array()+0.5); Dv.head(k).setConstant(1.); MatrixXd D = Dv.asDiagonal(); // Incorporate slack variables MatrixXd A(_A.rows(),_A.cols()+D.cols()); A<<_A,D; // Initial basis VectorXi B = igl::colon<int>(n,n+m-1); // non-basis, may turn out that vector<> would be better here VectorXi N = igl::colon<int>(0,n-1); int j; double bmin = b.minCoeff(&j); int phase; VectorXd xb; VectorXd s; VectorXi J; if(k>0 && bmin<0) { phase = 1; xb = VectorXd::Ones(m); // super cost s.resize(n+m+1); s<<VectorXd::Zero(n+k),VectorXd::Ones(m-k+1); N.resize(n+1); N<<igl::colon<int>(0,n-1),B(j); J.resize(B.size()-1); // [0 1 2 3 4] // ^ // [0 1] // [3 4] J.head(j) = B.head(j); J.tail(B.size()-j-1) = B.tail(B.size()-j-1); B(j) = n+m; MatrixXd AJ; igl::slice(A,J,2,AJ); const VectorXd a = b - AJ.rowwise().sum(); { MatrixXd old_A = A; A.resize(A.rows(),A.cols()+a.cols()); A<<old_A,a; } D.col(j) = -a/a(j); D(j,j) = 1./a(j); }else if(k==m) { phase = 2; xb = b; s.resize(c.size()+m); // cost function s<<c,VectorXd::Zero(m); }else //k = 0 or bmin >=0 { phase = 1; xb = b.array().abs(); s.resize(n+m); // super cost s<<VectorXd::Zero(n+k),VectorXd::Ones(m-k); } while(phase<3) { double df = -1; int t = std::numeric_limits<int>::max(); // Lagrange mutipliers fro Ax=b VectorXd yb = D.transpose() * igl::slice(s,B); while(true) { if(MAXIT>0 && it>=MAXIT) { #ifdef IGL_LINPROG_VERBOSE cerr<<"linprog: warning! maximum iterations without convergence."<<endl; #endif success = false; break; } // no freedom for minimization if(N.size() == 0) { break; } // reduced costs VectorXd sN = igl::slice(s,N); MatrixXd AN = igl::slice(A,N,2); VectorXd r = sN - AN.transpose() * yb; int q; // determine new basic variable double rmin = r.minCoeff(&q); // optimal! infinity norm if(rmin>=-tol*(sN.array().abs().maxCoeff()+1)) { break; } // increment iteration count it++; // apply Bland's rule to avoid cycling if(df>=0) { if(MAXIT == -1) { #ifdef IGL_LINPROG_VERBOSE cerr<<"linprog: warning! degenerate vertex"<<endl; #endif success = false; } igl::find((r.array()<0).eval(),J); double Nq = igl::slice(N,J).minCoeff(); // again seems like q is assumed to be a scalar though matlab code // could produce a vector for multiple matches (N.array()==Nq).cast<int>().maxCoeff(&q); } VectorXd d = D*A.col(N(q)); VectorXi I; igl::find((d.array()>tol).eval(),I); if(I.size() == 0) { #ifdef IGL_LINPROG_VERBOSE cerr<<"linprog: warning! solution is unbounded"<<endl; #endif // This seems dubious: it=-it; success = false; break; } VectorXd xbd = igl::slice(xb,I).array()/igl::slice(d,I).array(); // new use of r int p; { double r; r = xbd.minCoeff(&p); p = I(p); // apply Bland's rule to avoid cycling if(df>=0) { igl::find((xbd.array()==r).eval(),J); double Bp = igl::slice(B,igl::slice(I,J)).minCoeff(); // idiotic way of finding index in B of Bp // code down the line seems to assume p is a scalar though the matlab // code could find a vector of matches) (B.array()==Bp).cast<int>().maxCoeff(&p); } // update x xb -= r*d; xb(p) = r; // change in f df = r*rmin; } // row vector RowVectorXd v = D.row(p)/d(p); yb += v.transpose() * (s(N(q)) - d.transpose()*igl::slice(s,B)); d(p)-=1; // update inverse basis matrix D = D - d*v; t = B(p); B(p) = N(q); if(t>(n+k-1)) { // remove qth entry from N VectorXi old_N = N; N.resize(N.size()-1); N.head(q) = old_N.head(q); N.head(q) = old_N.head(q); N.tail(old_N.size()-q-1) = old_N.tail(old_N.size()-q-1); }else { N(q) = t; } } // iterative refinement xb = (xb+D*(b-igl::slice(A,B,2)*xb)).eval(); // must be due to rounding VectorXi I; igl::find((xb.array()<0).eval(),I); if(I.size()>0) { // so correct VectorXd Z = VectorXd::Zero(I.size(),1); igl::slice_into(Z,I,xb); } // B, xb,n,m,res=A(:,B)*xb-b if(phase == 2 || it<0) { break; } if(xb.transpose()*igl::slice(s,B) > tol) { it = -it; #ifdef IGL_LINPROG_VERBOSE cerr<<"linprog: warning, no feasible solution"<<endl; #endif success = false; break; } // re-initialize for Phase 2 phase = phase+1; s*=1e6*c.array().abs().maxCoeff(); s.head(n) = c; } x.resize(std::max(B.maxCoeff()+1,n)); igl::slice_into(xb,B,x); x = x.head(n).eval(); return success; }
bool KMeans::onlineUpdate(MatrixXd& X, MatrixXd& C, VectorXi& idx) { // Initialize some cluster information prior to phase two MatrixXd Xmid1; MatrixXd Xmid2; if (m_sDistance.compare("cityblock") == 0) { Xmid1 = MatrixXd::Zero(k,p); Xmid2 = MatrixXd::Zero(k,p); for(qint32 i = 0; i < k; ++i) { if (m[i] > 0) { // Separate out sorted coords for points in i'th cluster, // and save values above and below median, component-wise MatrixXd Xsorted(m[i],p); qint32 c = 0; for(qint32 j = 0; j < idx.rows(); ++j) { if(idx[j] == i) { Xsorted.row(c) = X.row(j); ++c; } } for(qint32 j = 0; j < Xsorted.cols(); ++j) std::sort(Xsorted.col(j).data(),Xsorted.col(j).data()+Xsorted.rows()); qint32 nn = floor(0.5*m[i])-1; if ((m[i] % 2) == 0) { Xmid1.row(i) = Xsorted.row(nn); Xmid2.row(i) = Xsorted.row(nn+1); } else if (m[i] > 1) { Xmid1.row(i) = Xsorted.row(nn); Xmid2.row(i) = Xsorted.row(nn+2); } else { Xmid1.row(i) = Xsorted.row(0); Xmid2.row(i) = Xsorted.row(0); } } } } else if (m_sDistance.compare("hamming") == 0) { // Xsum = zeros(k,p); // for i = 1:k // if m(i) > 0 // % Sum coords for points in i'th cluster, component-wise // Xsum(i,:) = sum(X(idx==i,:), 1); // end // end } // // Begin phase two: single reassignments // VectorXi changed = VectorXi(m.rows()); qint32 count = 0; for(qint32 i = 0; i < m.rows(); ++i) { if(m[i] > 0) { changed[count] = i; ++count; } } changed.conservativeResize(count); qint32 lastmoved = 0; qint32 nummoved = 0; qint32 iter1 = iter; bool converged = false; while (iter < m_iMaxit) { // Calculate distances to each cluster from each point, and the // potential change in total sum of errors for adding or removing // each point from each cluster. Clusters that have not changed // membership need not be updated. // // Singleton clusters are a special case for the sum of dists // calculation. Removing their only point is never best, so the // reassignment criterion had better guarantee that a singleton // point will stay in its own cluster. Happily, we get // Del(i,idx(i)) == 0 automatically for them. if (m_sDistance.compare("sqeuclidean") == 0) { for(qint32 j = 0; j < changed.rows(); ++j) { qint32 i = changed[j]; VectorXi mbrs = VectorXi::Zero(idx.rows()); for(qint32 l = 0; l < idx.rows(); ++l) if(idx[l] == i) mbrs[l] = 1; VectorXi sgn = 1 - 2 * mbrs.array(); // -1 for members, 1 for nonmembers if (m[i] == 1) for(qint32 l = 0; l < mbrs.rows(); ++l) if(mbrs[l]) sgn[l] = 0; // prevent divide-by-zero for singleton mbrs Del.col(i) = ((double)m[i] / ((double)m[i] + sgn.cast<double>().array())); Del.col(i).array() *= (X - C.row(i).replicate(n,1)).array().pow(2).rowwise().sum().array(); } } else if (m_sDistance.compare("cityblock") == 0) { for(qint32 j = 0; j < changed.rows(); ++j) { qint32 i = changed[j]; if (m(i) % 2 == 0) // this will never catch singleton clusters { MatrixXd ldist = Xmid1.row(i).replicate(n,1) - X; MatrixXd rdist = X - Xmid2.row(i).replicate(n,1); VectorXd mbrs = VectorXd::Zero(idx.rows()); for(qint32 l = 0; l < idx.rows(); ++l) if(idx[l] == i) mbrs[l] = 1; MatrixXd sgn = ((-2*mbrs).array() + 1).replicate(1, p); // -1 for members, 1 for nonmembers rdist = sgn.array()*rdist.array(); ldist = sgn.array()*ldist.array(); for(qint32 l = 0; l < idx.rows(); ++l) { double sum = 0; for(qint32 h = 0; h < rdist.cols(); ++h) sum += rdist(l,h) > ldist(l,h) ? rdist(l,h) < 0 ? 0 : rdist(l,h) : ldist(l,h) < 0 ? 0 : ldist(l,h); Del(l,i) = sum; } } else Del.col(i) = ((X - C.row(i).replicate(n,1)).array().abs()).rowwise().sum(); } } else if (m_sDistance.compare("cosine") == 0 || m_sDistance.compare("correlation") == 0) { // The points are normalized, centroids are not, so normalize them MatrixXd normC = C.array().pow(2).rowwise().sum().sqrt(); // if any(normC < eps(class(normC))) % small relative to unit-length data points // error('Zero cluster centroid created at iteration %d during replicate %d.',iter, rep); // end // This can be done without a loop, but the loop saves memory allocations MatrixXd XCi; qint32 i; for(qint32 j = 0; j < changed.rows(); ++j) { i = changed[j]; XCi = X * C.row(i).transpose(); VectorXi mbrs = VectorXi::Zero(idx.rows()); for(qint32 l = 0; l < idx.rows(); ++l) if(idx[l] == i) mbrs[l] = 1; VectorXi sgn = 1 - 2 * mbrs.array(); // -1 for members, 1 for nonmembers double A = (double)m[i] * normC(i,0); double B = pow(((double)m[i] * normC(i,0)),2); Del.col(i) = 1 + sgn.cast<double>().array()* (A - (B + 2 * sgn.cast<double>().array() * m[i] * XCi.array() + 1).sqrt()); std::cout << "Del.col(i)\n" << Del.col(i) << std::endl; // Del(:,i) = 1 + sgn .*... // (m(i).*normC(i) - sqrt((m(i).*normC(i)).^2 + 2.*sgn.*m(i).*XCi + 1)); } } else if (m_sDistance.compare("hamming") == 0) { // for i = changed // if mod(m(i),2) == 0 % this will never catch singleton clusters // % coords with an unequal number of 0s and 1s have a // % different contribution than coords with an equal // % number // unequal01 = find(2*Xsum(i,:) ~= m(i)); // numequal01 = p - length(unequal01); // mbrs = (idx == i); // Di = abs(X(:,unequal01) - C(repmat(i,n,1),unequal01)); // Del(:,i) = (sum(Di, 2) + mbrs*numequal01) / p; // else // Del(:,i) = sum(abs(X - C(repmat(i,n,1),:)), 2) / p; // end // end } // Determine best possible move, if any, for each point. Next we // will pick one from those that actually did move. previdx = idx; prevtotsumD = totsumD; VectorXi nidx = VectorXi::Zero(Del.rows()); VectorXd minDel = VectorXd::Zero(Del.rows()); for(qint32 i = 0; i < Del.rows(); ++i) minDel[i] = Del.row(i).minCoeff(&nidx[i]); VectorXi moved = VectorXi::Zero(previdx.rows()); qint32 count = 0; for(qint32 i = 0; i < moved.rows(); ++i) { if(previdx[i] != nidx[i]) { moved[count] = i; ++count; } } moved.conservativeResize(count); if (moved.sum() > 0) { // Resolve ties in favor of not moving VectorXi moved_new = VectorXi::Zero(moved.rows()); count = 0; for(qint32 i = 0; i < moved.rows(); ++i) { if ( Del.array()(previdx[moved(i)]*n + moved(i)) > minDel(moved(i))) { moved_new[count] = moved[i]; ++count; } } moved_new.conservativeResize(count); moved = moved_new; } if (moved.rows() <= 0) { // Count an iteration if phase 2 did nothing at all, or if we're // in the middle of a pass through all the points if ((iter == iter1) || nummoved > 0) { ++iter; // printf("%6d\t%6d\t%8d\t%12g\n",iter,2,nummoved,totsumD); } converged = true; break; } // Pick the next move in cyclic order VectorXi moved_new(moved.rows()); for(qint32 i = 0; i < moved.rows(); ++i) moved_new[i] = ((moved[i] - lastmoved) % n) + lastmoved; moved[0] = moved_new.minCoeff() % n;//+1 moved.conservativeResize(1); // If we've gone once through all the points, that's an iteration if (moved[0] <= lastmoved) { ++iter; // printf("%6d\t%6d\t%8d\t%12g\n",iter,2,nummoved,totsumD); if(iter >= m_iMaxit) break; nummoved = 0; } ++nummoved; lastmoved = moved[0]; qint32 oidx = idx(moved[0]); nidx[0] = nidx(moved[0]); nidx.conservativeResize(1); totsumD += Del(moved[0],nidx[0]) - Del(moved[0],oidx); // Update the cluster index vector, and the old and new cluster // counts and centroids idx[ moved[0] ] = nidx[0]; m( nidx[0] ) = m( nidx[0] ) + 1; m( oidx ) = m( oidx ) - 1; if (m_sDistance.compare("sqeuclidean") == 0) { C.row(nidx[0]) = C.row(nidx[0]).array() + (X.row(moved[0]) - C.row(nidx[0])).array() / m[nidx[0]]; C.row(oidx) = C.row(oidx).array() - (X.row(moved[0]) - C.row(oidx)).array() / m[oidx]; } else if (m_sDistance.compare("cityblock") == 0) { VectorXi onidx(2); onidx << oidx, nidx[0];//ToDo always right? qint32 i; for(qint32 h = 0; h < 2; ++h) { i = onidx[h]; // Separate out sorted coords for points in each cluster. // New centroid is the coord median, save values above and // below median. All done component-wise. MatrixXd Xsorted(m[i],p); qint32 c = 0; for(qint32 j = 0; j < idx.rows(); ++j) { if(idx[j] == i) { Xsorted.row(c) = X.row(j); ++c; } } for(qint32 j = 0; j < Xsorted.cols(); ++j) std::sort(Xsorted.col(j).data(),Xsorted.col(j).data()+Xsorted.rows()); qint32 nn = floor(0.5*m[i])-1; if ((m[i] % 2) == 0) { C.row(i) = 0.5 * (Xsorted.row(nn) + Xsorted.row(nn+1)); Xmid1.row(i) = Xsorted.row(nn); Xmid2.row(i) = Xsorted.row(nn+1); } else { C.row(i) = Xsorted.row(nn+1); if (m(i) > 1) { Xmid1.row(i) = Xsorted.row(nn); Xmid2.row(i) = Xsorted.row(nn+2); } else { Xmid1.row(i) = Xsorted.row(0); Xmid2.row(i) = Xsorted.row(0); } } } } else if (m_sDistance.compare("cosine") == 0 || m_sDistance.compare("correlation") == 0) { C.row(nidx[0]).array() += (X.row(moved[0]) - C.row(nidx[0])).array() / m[nidx[0]]; C.row(oidx).array() += (X.row(moved[0]) - C.row(oidx)).array() / m[oidx]; } else if (m_sDistance.compare("hamming") == 0) { // % Update summed coords for points in each cluster. New // % centroid is the coord median. All done component-wise. // Xsum(nidx,:) = Xsum(nidx,:) + X(moved,:); // Xsum(oidx,:) = Xsum(oidx,:) - X(moved,:); // C(nidx,:) = .5*sign(2*Xsum(nidx,:) - m(nidx)) + .5; // C(oidx,:) = .5*sign(2*Xsum(oidx,:) - m(oidx)) + .5; } VectorXi sorted_onidx(1+nidx.rows()); sorted_onidx << oidx, nidx; std::sort(sorted_onidx.data(), sorted_onidx.data()+sorted_onidx.rows()); changed = sorted_onidx; } // phase two return converged; } // nested function