123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- // This file is part of libigl, a simple c++ geometry processing library.
- //
- // Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
- //
- // This Source Code Form is subject to the terms of the Mozilla Public License
- // v. 2.0. If a copy of the MPL was not distributed with this file, You can
- // obtain one at http://mozilla.org/MPL/2.0/.
- #include "linprog.h"
- #include "slice.h"
- #include "slice_into.h"
- #include "find.h"
- #include "matlab_format.h"
- #include "colon.h"
- #include <iostream>
- //#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;
- }
- IGL_INLINE bool igl::linprog(
- const Eigen::VectorXd & f,
- const Eigen::MatrixXd & A,
- const Eigen::VectorXd & b,
- const Eigen::MatrixXd & B,
- const Eigen::VectorXd & c,
- Eigen::VectorXd & x)
- {
- using namespace Eigen;
- using namespace std;
- const int m = A.rows();
- const int n = A.cols();
- const int p = B.rows();
- MatrixXd Im = MatrixXd::Identity(m,m);
- MatrixXd AS(m,n+m);
- AS<<A,Im;
- MatrixXd bS = b.array().abs();
- for(int i = 0;i<m;i++)
- {
- const auto & sign = [](double x)->double
- {
- return (x<0?-1:(x>0?1:0));
- };
- AS.row(i) *= sign(b(i));
- }
- MatrixXd In = MatrixXd::Identity(n,n);
- MatrixXd P(n+m,2*n+m);
- P<< In, -In, MatrixXd::Zero(n,m),
- MatrixXd::Zero(m,2*n), Im;
- MatrixXd ASP = AS*P;
- MatrixXd BSP(0,2*n+m);
- if(p>0)
- {
- MatrixXd BS(p,2*n);
- BS<<B,MatrixXd::Zero(p,n);
- BSP = BS*P;
- }
- VectorXd fSP = VectorXd::Ones(2*n+m);
- fSP.head(2*n) = P.block(0,0,n,2*n).transpose()*f;
- const VectorXd & cc = fSP;
- MatrixXd AA(m+p,2*n+m);
- AA<<ASP,BSP;
- VectorXd bb(m+p);
- bb<<bS,c;
- VectorXd xxs;
- bool ret = linprog(cc,AA,bb,0,xxs);
- x = P.block(0,0,n,2*n+m)*xxs;
- return ret;
- }
|