• Main Page
  • Related Pages
  • Classes
  • Files
  • Examples
  • File List
  • File Members

QuadraticProgram.cpp

Go to the documentation of this file.
00001 //===========================================================================
00035 //===========================================================================
00036 
00037 
00038 #include <SharkDefs.h>
00039 #include <Rng/GlobalRng.h>
00040 #include <Array/ArrayIo.h>
00041 #include <Array/ArrayOp.h>
00042 #include <LinAlg/LinAlg.h>
00043 #include <ReClaM/QuadraticProgram.h>
00044 
00045 #include <math.h>
00046 #include <time.h>
00047 #include <iostream>
00048 #include <iomanip>
00049 
00050 
00051 using namespace std;
00052 
00053 
00054 // useful exchange macros for Array<T> and std::vector<T>
00055 #define XCHG_A(t, a, i, j) {t temp; temp = a(i); a(i) = a(j); a(j) = temp;}
00056 #define XCHG_V(t, a, i, j) {t temp; temp = a[i]; a[i] = a[j]; a[j] = temp;}
00057 
00058 
00060 
00061 
00062 QPSolver::QPSolver()
00063 {
00064 }
00065 
00066 QPSolver::~QPSolver()
00067 {
00068 }
00069 
00070 
00072 
00073 
00074 QPMatrix::QPMatrix(unsigned int size)
00075 {
00076     matrixsize = size;
00077 }
00078 
00079 QPMatrix::~QPMatrix()
00080 {
00081 }
00082 
00083 
00085 
00086 
00087 KernelMatrix::KernelMatrix(KernelFunction* kernelfunction,
00088                            const Array<double>& data)
00089 : QPMatrix(data.dim(0))
00090 , kernel(kernelfunction)
00091 {
00092     x.resize(matrixsize, false);
00093     unsigned int i;
00094     for (i = 0; i < matrixsize; i++)
00095     {
00096         x(i) = new ArrayReference<double>(data[i]);
00097     }
00098 }
00099 
00100 KernelMatrix::~KernelMatrix()
00101 {
00102     unsigned int i;
00103     for (i = 0; i < matrixsize; i++)
00104     {
00105         delete x(i);
00106     }
00107 }
00108 
00109 
00110 float KernelMatrix::Entry(unsigned int i, unsigned int j)
00111 {
00112     return (float)kernel->eval(*x(i), *x(j));
00113 }
00114 
00115 void KernelMatrix::FlipColumnsAndRows(unsigned int i, unsigned int j)
00116 {
00117     XCHG_A(ArrayReference<double>*, x, i, j);
00118 }
00119 
00120 
00122 
00123 
00124 RegularizedKernelMatrix::RegularizedKernelMatrix(KernelFunction* kernel,
00125         const Array<double>& data,
00126         const Array<double>& diagModification)
00127         : KernelMatrix(kernel, data)
00128 {
00129     SIZE_CHECK(data.dim(0) == diagModification.dim(0));
00130     diagMod = diagModification;
00131 }
00132 
00133 RegularizedKernelMatrix::~RegularizedKernelMatrix()
00134 {
00135 }
00136 
00137 
00138 float RegularizedKernelMatrix::Entry(unsigned int i, unsigned int j)
00139 {
00140     float ret = (float)KernelMatrix::Entry(i, j);
00141     if (i == j) ret += (float)diagMod(i);
00142     return ret;
00143 }
00144 
00145 void RegularizedKernelMatrix::FlipColumnsAndRows(unsigned int i, unsigned int j)
00146 {
00147     KernelMatrix::FlipColumnsAndRows(i, j);
00148 
00149     XCHG_A(double, diagMod, i, j);
00150 }
00151 
00152 
00154 
00155 
00156 QPMatrix2::QPMatrix2(QPMatrix* base)
00157 : QPMatrix(2 * base->getMatrixSize())
00158 {
00159     baseMatrix = base;
00160 
00161     mapping.resize(matrixsize, false);
00162 
00163     unsigned int i, ic = base->getMatrixSize();
00164     for (i = 0; i < ic; i++)
00165     {
00166         mapping(i) = i;
00167         mapping(i + ic) = i;
00168     }
00169 }
00170 
00171 QPMatrix2::~QPMatrix2()
00172 {
00173     delete baseMatrix;
00174 }
00175 
00176 
00177 float QPMatrix2::Entry(unsigned int i, unsigned int j)
00178 {
00179     return baseMatrix->Entry(mapping(i), mapping(j));
00180 }
00181 
00182 void QPMatrix2::FlipColumnsAndRows(unsigned int i, unsigned int j)
00183 {
00184     XCHG_A(unsigned int, mapping, i, j);
00185 }
00186 
00187 
00189 
00190 
00191 InputLabelMatrix::InputLabelMatrix(KernelFunction* kernelfunction,
00192                         const Array<double>& input,
00193                         const Array<double>& target,
00194                         const Array<double>& prototypes)
00195 : QPMatrix(input.dim(0))
00196 , kernel(kernelfunction)
00197 {
00198     unsigned int i, j, m, classes = prototypes.dim(0);
00199 
00200     x.resize(matrixsize, false);
00201     y.resize(matrixsize);
00202     for (i=0; i < matrixsize; i++)
00203     {
00204         x(i) = new ArrayReference<double>(input[i]);
00205         y[i] = (unsigned int)target(i, 0);
00206     }
00207 
00208     prototypeProduct.resize(classes, classes, false);
00209     for (i=0; i<classes; i++)
00210     {
00211         for (j=0; j<=i; j++)
00212         {
00213             double scp = 0.0;
00214             for (m=0; m<classes; m++) scp += prototypes(i, m) * prototypes(j, m);
00215             prototypeProduct(i, j) = prototypeProduct(j, i) = scp;
00216         }
00217     }
00218 }
00219 
00220 InputLabelMatrix::~InputLabelMatrix()
00221 {
00222     unsigned int i;
00223     for (i = 0; i < matrixsize; i++) delete x(i);
00224 }
00225 
00226 
00227 float InputLabelMatrix::Entry(unsigned int i, unsigned int j)
00228 {
00229     return (float)(kernel->eval(*x(i), *x(j)) * prototypeProduct(y[i], y[j]));
00230 }
00231 
00232 void InputLabelMatrix::FlipColumnsAndRows(unsigned int i, unsigned int j)
00233 {
00234     XCHG_A(ArrayReference<double>*, x, i, j);
00235     XCHG_V(unsigned int, y, i, j);
00236 }
00237 
00238 
00240 
00241 
00242 CachedMatrix::CachedMatrix(QPMatrix* base, unsigned int cachesize)
00243         : QPMatrix(base->getMatrixSize())
00244 {
00245     baseMatrix = base;
00246 
00247     cacheSize = 0;
00248     cacheMaxSize = cachesize;
00249     cacheNewest = -1;
00250     cacheOldest = -1;
00251 
00252     cacheEntry.resize(matrixsize);
00253     unsigned int i;
00254     for (i = 0; i < matrixsize; i++)
00255     {
00256         cacheEntry[i].data = NULL;
00257         cacheEntry[i].length = 0;
00258         cacheEntry[i].older = -2;
00259         cacheEntry[i].newer = -2;
00260     }
00261     if (cachesize < 2 * matrixsize) throw SHARKEXCEPTION("[CachedMatrix::CachedMatrix] invalid cache size");
00262 
00263 }
00264 
00265 CachedMatrix::~CachedMatrix()
00266 {
00267     cacheClear();
00268 }
00269 
00270 
00271 float* CachedMatrix::Row(unsigned int k, unsigned int begin, unsigned int end, bool temp)
00272 {
00273     if (temp)
00274     {
00275         // return temporary data
00276         cacheTemp.resize(end);
00277         if (cacheEntry[k].length > (int)begin)
00278         {
00279             memcpy(&cacheTemp[0] + begin, cacheEntry[k].data + begin, sizeof(float) * (cacheEntry[k].length - begin));
00280             begin = cacheEntry[k].length;
00281         }
00282         unsigned int col;
00283         for (col = begin; col < end; col++) cacheTemp[col] = baseMatrix->Entry(k, col);
00284         return &cacheTemp[0];
00285     }
00286     else
00287     {
00288         // the data will be stored in the cache
00289         unsigned int l = cacheEntry[k].length;
00290         while (cacheSize + end > cacheMaxSize + l)
00291         {
00292             if (cacheOldest == (int)k)
00293             {
00294                 cacheRemove(k);
00295                 cacheAppend(k);
00296             }
00297             cacheDelete(cacheOldest);
00298         }
00299         if (l == 0)
00300         {
00301             cacheAdd(k, end);
00302         }
00303         else
00304         {
00305             cacheResize(k, end);
00306             if ((int)k != cacheNewest)
00307             {
00308                 cacheRemove(k);
00309                 cacheAppend(k);
00310             }
00311         }
00312 
00313         // compute remaining entries
00314         if (l < end)
00315         {
00316             float* p = cacheEntry[k].data + l;
00317             unsigned int col;
00318             for (col = l; col < end; col++)
00319             {
00320                 *p = baseMatrix->Entry(k, col);
00321                 p++;
00322             }
00323         }
00324 
00325         return cacheEntry[k].data;
00326     }
00327 }
00328 
00329 float CachedMatrix::Entry(unsigned int i, unsigned int j)
00330 {
00331     return baseMatrix->Entry(i, j);
00332 }
00333 
00334 void CachedMatrix::FlipColumnsAndRows(unsigned int i, unsigned int j)
00335 {
00336     int t;
00337 
00338     baseMatrix->FlipColumnsAndRows(i, j);
00339 
00340     // update the ordered cache list predecessors and successors
00341     t = cacheEntry[i].older;
00342     if (t != -2)
00343     {
00344         if (t == -1) cacheOldest = j;
00345         else cacheEntry[t].newer = j;
00346         t = cacheEntry[i].newer;
00347         if (t == -1) cacheNewest = j;
00348         else cacheEntry[t].older = j;
00349     }
00350     t = cacheEntry[j].older;
00351     if (cacheEntry[j].older != -2)
00352     {
00353         if (t == -1) cacheOldest = i;
00354         else cacheEntry[t].newer = i;
00355         t = cacheEntry[j].newer;
00356         if (t == -1) cacheNewest = i;
00357         else cacheEntry[t].older = i;
00358     }
00359 
00360     // exchange the cache entries
00361     XCHG_V(tCacheEntry, cacheEntry, i, j);
00362 
00363     // exchange all cache row entries
00364     unsigned int k, l;
00365     for (k = 0; k < matrixsize; k++)
00366     {
00367         l = cacheEntry[k].length;
00368         if (j < l)
00369         {
00370             XCHG_V(float, cacheEntry[k].data, i, j);
00371         }
00372         else if (i < l)
00373         {
00374             // only one element is available from the cache
00375             cacheEntry[k].data[i] = baseMatrix->Entry(k, i);
00376         }
00377     }
00378 }
00379 
00380 void CachedMatrix::CacheRowResize(unsigned int k, unsigned int newsize)
00381 {
00382     if (cacheEntry[k].data == NULL) cacheAdd(k, newsize);
00383     else cacheResize(k, newsize);
00384 }
00385 
00386 void CachedMatrix::CacheRowRelease(unsigned int k)
00387 {
00388     if (cacheEntry[k].data != NULL) cacheDelete(k);
00389 }
00390 
00391 void CachedMatrix::cacheAppend(int var)
00392 {
00393     if (cacheNewest == -1)
00394     {
00395         cacheNewest = var;
00396         cacheOldest = var;
00397         cacheEntry[var].older = -1;
00398         cacheEntry[var].newer = -1;
00399     }
00400     else
00401     {
00402         cacheEntry[cacheNewest].newer = var;
00403         cacheEntry[var].older = cacheNewest;
00404         cacheEntry[var].newer = -1;
00405         cacheNewest = var;
00406     }
00407 }
00408 
00409 void CachedMatrix::cacheRemove(int var)
00410 {
00411     if (cacheEntry[var].older == -1)
00412         cacheOldest = cacheEntry[var].newer;
00413     else
00414         cacheEntry[cacheEntry[var].older].newer = cacheEntry[var].newer;
00415 
00416     if (cacheEntry[var].newer == -1)
00417         cacheNewest = cacheEntry[var].older;
00418     else
00419         cacheEntry[cacheEntry[var].newer].older = cacheEntry[var].older;
00420 
00421     cacheEntry[var].older = -2;
00422     cacheEntry[var].newer = -2;
00423 }
00424 
00425 void CachedMatrix::cacheAdd(int var, unsigned int length)
00426 {
00427     cacheEntry[var].length = length;
00428     cacheEntry[var].data = (float*)(void*)malloc(length * sizeof(float));
00429     if (cacheEntry[var].data == NULL) throw SHARKEXCEPTION("[CachedMatrix::cacheAppend] out of memory error");
00430     cacheSize += length;
00431 
00432     cacheAppend(var);
00433 }
00434 
00435 void CachedMatrix::cacheDelete(int var)
00436 {
00437     free(cacheEntry[var].data);
00438     cacheSize -= cacheEntry[var].length;
00439 
00440     cacheEntry[var].data = NULL;
00441     cacheEntry[var].length = 0;
00442 
00443     cacheRemove(var);
00444 }
00445 
00446 void CachedMatrix::cacheResize(int var, unsigned int newlength)
00447 {
00448     int diff = newlength - cacheEntry[var].length;
00449     if (diff == 0) return;
00450     cacheSize += diff;
00451     cacheEntry[var].length = newlength;
00452     cacheEntry[var].data = (float*)(void*)realloc((void*)cacheEntry[var].data, newlength * sizeof(float));
00453     if (cacheEntry[var].data == NULL) throw SHARKEXCEPTION("[CachedMatrix::cacheResize] out of memory error");
00454 }
00455 
00456 void CachedMatrix::cacheClear()
00457 {
00458     unsigned int e, ec = cacheEntry.size();
00459     for (e = 0; e < ec; e++)
00460     {
00461         if (cacheEntry[e].data != NULL) free(cacheEntry[e].data);
00462         cacheEntry[e].data = NULL;
00463         cacheEntry[e].length = 0;
00464         cacheEntry[e].older = -2;
00465         cacheEntry[e].newer = -2;
00466     }
00467     cacheOldest = -1;
00468     cacheNewest = -1;
00469     cacheSize = 0;
00470 }
00471 
00472 
00474 
00475 
00476 void QpSvmCG::Solve(const Array<double>& quadratic,
00477         const Array<double>& linear,
00478         const Array<double>& boxMin,
00479         const Array<double>& boxMax,
00480         Array<double>& point,
00481         double accuracy)
00482 {
00483     SIZE_CHECK(quadratic.ndim() == 2);
00484     SIZE_CHECK(linear.ndim() == 1);
00485     SIZE_CHECK(boxMin.ndim() == 1);
00486     SIZE_CHECK(boxMax.ndim() == 1);
00487     SIZE_CHECK(point.ndim() == 1);
00488 
00489     int dimension = quadratic.dim(0);
00490 
00491     SIZE_CHECK((int)quadratic.dim(1) == dimension);
00492     SIZE_CHECK((int)linear.dim(0) == dimension);
00493     SIZE_CHECK((int)boxMin.dim(0) == dimension);
00494     SIZE_CHECK((int)boxMax.dim(0) == dimension);
00495     SIZE_CHECK((int)point.dim(0) == dimension);
00496 
00497     int i, j;
00498     Array<double> direction(dimension);
00499     Array<bool> active(dimension);
00500     Array<double> gradient(dimension);
00501     Array<double> conjugate(dimension);
00502     conjugate = 0.0;
00503     double DirLen2 = 0.0;
00504     double DirLen2old = 0.0;
00505     double accuracy2 = accuracy * accuracy;
00506     bool changed, bounds_changed;
00507     int nActive = 0;
00508     int nUpdatesLeft = -1;
00509     int bound = -1;
00510     int nReset = 0;
00511     double step = 0.0;
00512     double clipped = 0.0;
00513     double value;
00514     double first = 0.0;
00515     double second = 0.0;
00516     double scp, sub;
00517     double norm2;
00518 
00519     active = false;
00520 
00521     int iter, maxiter = 10 * dimension * dimension;
00522     for (iter=0; iter<maxiter; iter++)
00523     {
00524         if (nUpdatesLeft == -1)
00525         {
00526             // compute the gradient from scratch
00527             for (i=0; i<dimension; i++)
00528             {
00529                 value = linear(i);
00530                 for (j=0; j<dimension; j++) value += quadratic(i, j) * point(j);
00531                 gradient(i) = value;
00532             }
00533             nUpdatesLeft = 100;
00534         }
00535 
00536         // compute the set of active inequality constraints
00537         scp = 0.0;
00538         for (i=0; i<dimension; i++)
00539         {
00540             if (active(i)) continue;
00541             scp += gradient(i);
00542         }
00543         sub = scp / (dimension - nActive);
00544         bounds_changed = false;
00545         do
00546         {
00547             changed = false;
00548             for (i=0; i<dimension; i++)
00549             {
00550                 if (active(i))
00551                 {
00552                     double g = gradient(i) - (scp + gradient(i)) / (dimension - nActive + 1.0);
00553                     if ((g <= 0.0 && point(i) == boxMin(i))
00554                             || (g >= 0.0 && point(i) == boxMax(i)))
00555                     {
00556                         active(i) = false;
00557                         nActive--;
00558                         scp += gradient(i);
00559                         sub = scp / (dimension - nActive);
00560                         changed = true;
00561                     }
00562                 }
00563                 else
00564                 {
00565                     double g = gradient(i) - sub;
00566                     if ((g > 0.0 && point(i) == boxMin(i))
00567                             || (g < 0.0 && point(i) == boxMax(i)))
00568                     {
00569                         active(i) = true;
00570                         nActive++;
00571                         scp -= gradient(i);
00572                         sub = scp / (dimension - nActive);
00573                         changed = true;
00574                     }
00575                 }
00576             }
00577             bounds_changed = bounds_changed || changed;
00578         }
00579         while (changed);
00580         if (bounds_changed && iter > 0) nReset = 0;
00581 
00582         // check corner condition
00583         if (nActive + 1 >= dimension) break;
00584 
00585         // compute the gradient of the sub problem
00586         // projected onto the equality constraint
00587         DirLen2old = DirLen2;
00588         DirLen2 = 0.0;
00589         for (i=0; i<dimension; i++)
00590         {
00591             if (active(i)) continue;
00592             value = gradient(i) - sub;
00593             direction(i) = value;
00594             DirLen2 += value * value;
00595         }
00596 
00597         // update the conjugate direction
00598         nReset--;
00599         if (nReset == 0)
00600         {
00601             // In theory we are done.
00602             // However, we may which to continue
00603             // in order to avoid numerical problems.
00604             break;
00605         }
00606         else if (nReset < 0)
00607         {
00608             // the set of active constraints has changed
00609             conjugate = direction;
00610             nReset = dimension - 1 - nActive;
00611         }
00612         else
00613         {
00614             double beta = DirLen2 / DirLen2old;
00615             for (i=0; i<dimension; i++)
00616             {
00617                 if (active(i)) continue;
00618                 else conjugate(i) = direction(i) + beta * conjugate(i);
00619             }
00620         }
00621 
00622         // compute the unconstrained Newton step
00623         first = 0.0;
00624         second = 0.0;
00625         norm2 = 0.0;
00626         for (i=0; i<dimension; i++)
00627         {
00628             if (active(i)) continue;
00629             double c = conjugate(i);
00630             double Qc = 0.0;
00631             for (j=0; j<dimension; j++)
00632             {
00633                 if (active(j)) continue;
00634                 Qc += quadratic(i, j) * conjugate(j);
00635             }
00636             first += gradient(i) * c;
00637             second += c * Qc;
00638             norm2 += c * c;
00639         }
00640         if (second <= 0.0) break;
00641         step = -first / second;
00642 
00643         // check stopping condition
00644         if (nReset <= 1 && step * step * norm2 < accuracy2) break;
00645 
00646         // clip the step
00647         bound = -1;
00648         clipped = step;
00649         for (i=0; i<dimension; i++)
00650         {
00651             if (active(i)) continue;
00652             if (point(i) + clipped * conjugate(i) > boxMax(i))
00653             {
00654                 clipped = (boxMax(i) - point(i)) / conjugate(i);
00655                 bound = i;
00656             }
00657             else if (point(i) + clipped * conjugate(i) < boxMin(i))
00658             {
00659                 clipped = (boxMin(i) - point(i)) / conjugate(i);
00660                 bound = i;
00661             }
00662         }
00663 
00664         // update the gradient to avoid full recomputation
00665         if (5 * nActive > dimension && nUpdatesLeft > 0)
00666         {
00667             // update the gradient
00668             for (i=0; i<dimension; i++)
00669             {
00670                 if (active(i)) continue;
00671                 double delta = clipped * conjugate(i);
00672                 for (j=0; j<dimension; j++) gradient(j) += quadratic(i, j) * delta;
00673             }
00674             nUpdatesLeft--;
00675         }
00676         else nUpdatesLeft = -1;
00677 
00678         // go!
00679         changed = false;
00680         for (i=0; i<dimension; i++)
00681         {
00682             if (active(i)) continue;
00683             double p = point(i);
00684             double newp = p + clipped * conjugate(i);
00685             point(i) = newp;
00686             if (p != newp) changed = true;
00687         }
00688 
00689         // stop if the step has no numerical effect
00690         if (! changed) break;
00691 
00692         // check the bound carefully
00693         if (bound != -1)
00694         {
00695 //          if (point(bound) > (1.0 - 1e-12) * boxMax(bound)) point(bound) = boxMax(bound);
00696 //          else if (point(bound) < (1.0 + 1e-12) * boxMin(bound)) point(bound) = boxMin(bound);
00697             double threshold = 0.5 * (boxMax(bound) + boxMin(bound));
00698             if (point(bound) > threshold) point(bound) = boxMax(bound);
00699             else point(bound) = boxMin(bound);
00700             nReset = 0;
00701         }
00702     }
00703 
00704     if (iter >= maxiter)
00705     {
00706         throw SHARKEXCEPTION("QpSvmCG did not converge");
00707         printf("\n\n***************** QpSvmCG did not converge!!!\n\n");
00708         printf("boxMin:\n");
00709         writeArray(boxMin, std::cout);
00710         printf("boxMax:\n");
00711         writeArray(boxMax, std::cout);
00712         printf("point:\n");
00713         writeArray(point, std::cout);
00714         printf("gradient:\n");
00715         writeArray(gradient, std::cout);
00716         printf("direction:\n");
00717         writeArray(direction, std::cout);
00718         printf("conjugate:\n");
00719         writeArray(conjugate, std::cout);
00720         printf("active:\n");
00721         writeArray(active, std::cout);
00722     }
00723 }
00724 
00725 
00727 
00728 
00729 QpSvmDecomp::QpSvmDecomp(CachedMatrix& quadraticPart)
00730 : quadratic(quadraticPart)
00731 {
00732     printInfo = false;
00733     WSS_Strategy = NULL;
00734     useShrinking = true;
00735     maxIter = -1;
00736     maxSeconds = -1;
00737 
00738     dimension = quadratic.getMatrixSize();
00739 
00740     // prepare lists
00741     alpha.resize(dimension, false);
00742     diagonal.resize(dimension, false);
00743     permutation.resize(dimension, false);
00744     gradient.resize(dimension, false);
00745     linear.resize(dimension, false);
00746     boxMin.resize(dimension, false);
00747     boxMax.resize(dimension, false);
00748 
00749     // prepare the permutation and the diagonal
00750     unsigned int i;
00751     for (i = 0; i < dimension; i++)
00752     {
00753         permutation(i) = i;
00754         diagonal(i) = quadratic.Entry(i, i);
00755     }
00756 }
00757 
00758 QpSvmDecomp::~QpSvmDecomp()
00759 {
00760 }
00761 
00762 
00763 double QpSvmDecomp::Solve(const Array<double>& linearPart,
00764                                const Array<double>& boxLower,
00765                                const Array<double>& boxUpper,
00766                                Array<double>& solutionVector,
00767                                double eps,
00768                                double threshold)
00769 {
00770     SIZE_CHECK(linearPart.ndim() == 1);
00771     SIZE_CHECK(boxLower.ndim() == 1);
00772     SIZE_CHECK(boxUpper.ndim() == 1);
00773     SIZE_CHECK(linearPart.dim(0) == dimension);
00774     SIZE_CHECK(boxLower.dim(0) == dimension);
00775     SIZE_CHECK(boxUpper.dim(0) == dimension);
00776 
00777     unsigned int a, i, j;
00778     float* qi;
00779     float* qj;
00780 
00781     for (i = 0; i < dimension; i++)
00782     {
00783         j = permutation(i);
00784         alpha(i) = solutionVector(j);
00785         linear(i) = linearPart(j);
00786         boxMin(i) = boxLower(j);
00787         boxMax(i) = boxUpper(j);
00788     }
00789 
00790     epsilon = eps;
00791     optimal = false;
00792 
00793     // prepare the solver internal variables
00794     active = dimension;
00795     gradient = linear;
00796 
00797     for (i = 0; i < dimension; i++)
00798     {
00799         if (boxMax(i) < boxMin(i)) throw SHARKEXCEPTION("[QpSvmDecomp::Solve] The feasible region is empty.");
00800 
00801         double v = alpha(i);
00802         if (v != 0.0)
00803         {
00804             qi = quadratic.Row(i, 0, dimension);
00805             for (a = 0; a < dimension; a++) gradient(a) -= qi[a] * v;
00806         }
00807     }
00808 
00809     bFirst = true;
00810     bUnshrinked = false;
00811     unsigned int shrinkCounter = (active < 1000) ? active : 1000;
00812 
00813     SelectWSS();
00814 
00815     // decomposition loop
00816     if (printInfo) cout << "{" << flush;
00817     iter = 0;
00818     time_t starttime;
00819     time(&starttime);
00820     while (iter != maxIter)
00821     {
00822         // select a working set and check for optimality
00823         if (SelectWorkingSet(i, j))
00824         {
00825             // seems to be optimal
00826             if (printInfo) cout << "*" << flush;
00827 
00828             if (! useShrinking)
00829             {
00830                 optimal = true;
00831                 break;
00832             }
00833 
00834             // do costly unshrinking
00835             Unshrink();
00836             shrinkCounter = 1;
00837 
00838             // check again on the whole problem
00839             if (SelectWorkingSet(i, j))
00840             {
00841                 optimal = true;
00842                 break;
00843             }
00844         }
00845 
00846         // SMO update
00847         {
00848             double ai = alpha(i);
00849             double aj = alpha(j);
00850             double Li = boxMin(i);
00851             double Ui = boxMax(i);
00852             double Lj = boxMin(j);
00853             double Uj = boxMax(j);
00854 
00855             // get the matrix rows corresponding to the working set
00856             qi = quadratic.Row(i, 0, active);
00857             qj = quadratic.Row(j, 0, active);
00858 
00859             // update alpha, that is, solve the sub-problem defined by i and j
00860             double nominator = gradient(i) - gradient(j);
00861             double denominator = diagonal(i) + diagonal(j) - 2.0 * qi[j];
00862             double mu = nominator / denominator;
00863             if (ai + mu < Li) mu = Li - ai;
00864             else if (ai + mu > Ui) mu = Ui - ai;
00865             if (aj - mu < Lj) mu = aj - Lj;
00866             else if (aj - mu > Uj) mu = aj - Uj;
00867             alpha(i) += mu;
00868             alpha(j) -= mu;
00869 
00870             // update the gradient
00871             for (a = 0; a < active; a++) gradient(a) -= mu * (qi[a] - qj[a]);
00872         }
00873 
00874         shrinkCounter--;
00875         if (shrinkCounter == 0)
00876         {
00877             // shrink the problem
00878             if (useShrinking) Shrink();
00879 
00880             shrinkCounter = (active < 1000) ? active : 1000;
00881 
00882             if (maxSeconds != -1)
00883             {
00884                 time_t currenttime;
00885                 time(&currenttime);
00886                 if (currenttime - starttime > maxSeconds) break;
00887             }
00888         }
00889 
00890         if (threshold < 1e100)
00891         {
00892             double objective = 0.0;
00893             for (i = 0; i < active; i++)
00894             {
00895                 objective += (gradient(i) + linear(i)) * alpha(i);
00896             }
00897             objective *= 0.5;
00898             if (objective > threshold) break;
00899         }
00900 
00901         iter++;
00902         if (printInfo)
00903         {
00904             if ((iter & 1023) == 0) cout << "." << flush;
00905         }
00906     }
00907     if (printInfo) cout << endl << "} #iterations=" << (long int)iter << endl;
00908 
00909     Unshrink(true);
00910 
00911     // return alpha and the objective value
00912     double objective = 0.0;
00913     for (i = 0; i < dimension; i++)
00914     {
00915         solutionVector(permutation(i)) = alpha(i);
00916         objective += (gradient(i) + linear(i)) * alpha(i);
00917     }
00918     return 0.5 * objective;
00919 }
00920 
00921 double QpSvmDecomp::ComputeInnerProduct(unsigned int index, const Array<double>& coeff)
00922 {
00923     unsigned int e = permutation(index);
00924     unsigned int i;
00925     unsigned int j;
00926     double ret = 0.0;
00927     double c;
00928     unsigned int crs_j, crs_e = quadratic.getCacheRowSize(e);
00929 
00930     for (i = 0; i < dimension; i++)
00931     {
00932         j = permutation(i);
00933         c = coeff(j);
00934         if (c != 0.0)
00935         {
00936             if (j < crs_e) ret += c * quadratic.Row(e, 0, crs_e)[j];
00937             else
00938             {
00939                 crs_j = quadratic.getCacheRowSize(j);
00940                 if (e < crs_j) ret += c * quadratic.Row(j, 0, crs_j)[e];
00941                 else ret += c * quadratic.Entry(j, e);
00942             }
00943         }
00944     }
00945 
00946     return ret;
00947 }
00948 
00949 void QpSvmDecomp::getGradient(Array<double>& grad)
00950 {
00951     grad.resize(dimension, false);
00952     unsigned int i;
00953     for (i = 0; i < dimension; i++) grad(permutation(i)) = gradient(i);
00954 }
00955 
00956 bool QpSvmDecomp::MVP(unsigned int& i, unsigned int& j)
00957 {
00958     double largestUp = -1e100;
00959     double smallestDown = 1e100;
00960     unsigned int a;
00961 
00962     for (a = 0; a < active; a++)
00963     {
00964         if (alpha(a) < boxMax(a))
00965         {
00966             if (gradient(a) > largestUp)
00967             {
00968                 largestUp = gradient(a);
00969                 i = a;
00970             }
00971         }
00972         if (alpha(a) > boxMin(a))
00973         {
00974             if (gradient(a) < smallestDown)
00975             {
00976                 smallestDown = gradient(a);
00977                 j = a;
00978             }
00979         }
00980     }
00981 
00982     // MVP stopping condition
00983     return (largestUp - smallestDown < epsilon);
00984 }
00985 
00986 bool QpSvmDecomp::HMG(unsigned int& i, unsigned int& j)
00987 {
00988     if (bFirst)
00989     {
00990         // the cache is empty - use MVP
00991         bFirst = false;
00992 //      return MVP(i, j);           // original paper: use MVP
00993         return Libsvm28(i, j);      // better: use second order algorithm
00994     }
00995 
00996     // check the corner condition
00997     {
00998         double Li = boxMin(old_i);
00999         double Ui = boxMax(old_i);
01000         double Lj = boxMin(old_j);
01001         double Uj = boxMax(old_j);
01002         double eps_i = 1e-8 * (Ui - Li);
01003         double eps_j = 1e-8 * (Uj - Lj);
01004         if ((alpha(old_i) <= Li + eps_i || alpha(old_i) >= Ui - eps_i)
01005                 && ((alpha(old_j) <= Lj + eps_j || alpha(old_j) >= Uj - eps_j)))
01006         {
01007             if (printInfo) cout << "^" << flush;
01008 //          return MVP(i, j);           // original paper: use MVP
01009             return Libsvm28(i, j);      // better: use second order algorithm
01010         }
01011     }
01012 
01013     // generic situation: use the MG selection
01014     unsigned int a;
01015     double aa, ab;                  // alpha values
01016     double da, db;                  // diagonal entries of Q
01017     double ga, gb;                  // gradient in coordinates a and b
01018     double gain;
01019     double La, Ua, Lb, Ub;
01020     double denominator;
01021     float* q;
01022     double mu_max, mu_star;
01023 
01024     double best = 0.0;
01025     double mu_best = 0.0;
01026 
01027     // try combinations with b = old_i
01028     q = quadratic.Row(old_i, 0, active);
01029     ab = alpha(old_i);
01030     db = diagonal(old_i);
01031     Lb = boxMin(old_i);
01032     Ub = boxMax(old_i);
01033     gb = gradient(old_i);
01034     for (a = 0; a < active; a++)
01035     {
01036         if (a == old_i || a == old_j) continue;
01037 
01038         aa = alpha(a);
01039         da = diagonal(a);
01040         La = boxMin(a);
01041         Ua = boxMax(a);
01042         ga = gradient(a);
01043 
01044         denominator = (da + db - 2.0 * q[a]);
01045         mu_max = (ga - gb) / denominator;
01046         mu_star = mu_max;
01047 
01048         if (aa + mu_star < La) mu_star = La - aa;
01049         else if (mu_star + aa > Ua) mu_star = Ua - aa;
01050         if (ab - mu_star < Lb) mu_star = ab - Lb;
01051         else if (ab - mu_star > Ub) mu_star = ab - Ub;
01052 
01053         gain = mu_star * (2.0 * mu_max - mu_star) * denominator;
01054 
01055         // select the largest gain
01056         if (gain > best)
01057         {
01058             best = gain;
01059             mu_best = mu_star;
01060             i = a;
01061             j = old_i;
01062         }
01063     }
01064 
01065     // try combinations with old_j
01066     q = quadratic.Row(old_j, 0, active);
01067     ab = alpha(old_j);
01068     db = diagonal(old_j);
01069     Lb = boxMin(old_j);
01070     Ub = boxMax(old_j);
01071     gb = gradient(old_j);
01072     for (a = 0; a < active; a++)
01073     {
01074         if (a == old_i || a == old_j) continue;
01075 
01076         aa = alpha(a);
01077         da = diagonal(a);
01078         La = boxMin(a);
01079         Ua = boxMax(a);
01080         ga = gradient(a);
01081 
01082         denominator = (da + db - 2.0 * q[a]);
01083         mu_max = (ga - gb) / denominator;
01084         mu_star = mu_max;
01085 
01086         if (aa + mu_star < La) mu_star = La - aa;
01087         else if (mu_star + aa > Ua) mu_star = Ua - aa;
01088         if (ab - mu_star < Lb) mu_star = ab - Lb;
01089         else if (ab - mu_star > Ub) mu_star = ab - Ub;
01090 
01091         gain = mu_star * (2.0 * mu_max - mu_star) * denominator;
01092 
01093         // select the largest gain
01094         if (gain > best)
01095         {
01096             best = gain;
01097             mu_best = mu_star;
01098             i = a;
01099             j = old_j;
01100         }
01101     }
01102 
01103     // stopping condition
01104     return (fabs(mu_best) < epsilon);
01105 }
01106 
01107 bool QpSvmDecomp::Libsvm28(unsigned int& i, unsigned int& j)
01108 {
01109     i = 0;
01110     j = 1;
01111 
01112     double largestUp = -1e100;
01113     double smallestDown = 1e100;
01114     unsigned int a;
01115 
01116     // find the first index of the MVP
01117     for (a = 0; a < active; a++)
01118     {
01119         if (alpha(a) < boxMax(a))
01120         {
01121             if (gradient(a) > largestUp)
01122             {
01123                 largestUp = gradient(a);
01124                 i = a;
01125             }
01126         }
01127     }
01128 
01129     // find the second index using second order information
01130     float* q = quadratic.Row(i, 0, active);
01131     double best = 0.0;
01132     for (a = 0; a < active; a++)
01133     {
01134         if (alpha(a) > boxMin(a))
01135         {
01136             if (gradient(a) < smallestDown) smallestDown = gradient(a);
01137 
01138             double grad_diff = largestUp - gradient(a);
01139             if (grad_diff > 0.0)
01140             {
01141                 double quad_coef = diagonal(i) + diagonal(a) - 2.0 * q[a];
01142                 if (quad_coef == 0.0) continue;
01143                 double obj_diff = (grad_diff * grad_diff) / quad_coef;
01144 
01145                 if (obj_diff > best)
01146                 {
01147                     best = obj_diff;
01148                     j = a;
01149                 }
01150             }
01151         }
01152     }
01153 
01154     // MVP stopping condition
01155     return (largestUp - smallestDown < epsilon);
01156 }
01157 
01158 bool QpSvmDecomp::SelectWorkingSet(unsigned int& i, unsigned int& j)
01159 {
01160     // dynamic working set selection call
01161     bool ret = (this->*(this->currentWSS))(i, j);
01162 
01163     old_i = i;
01164     old_j = j;
01165     return ret;
01166 }
01167 
01168 void QpSvmDecomp::SelectWSS()
01169 {
01170     if (WSS_Strategy != NULL && strcmp(WSS_Strategy, "MVP") == 0)
01171     {
01172         // most violating pair, used e.g. in LIBSVM 2.71
01173         currentWSS = &QpSvmDecomp::MVP;
01174     }
01175     else if (WSS_Strategy != NULL && strcmp(WSS_Strategy, "HMG") == 0)
01176     {
01177         // hybrid maximum gain, suitable for large problems
01178         currentWSS = &QpSvmDecomp::HMG;
01179     }
01180     else if (WSS_Strategy != NULL && strcmp(WSS_Strategy, "LIBSVM28") == 0)
01181     {
01182         // LIBSVM 2.8 second order algorithm
01183         currentWSS = &QpSvmDecomp::Libsvm28;
01184     }
01185     else
01186     {
01187         // default strategy:
01188         // use HMG as long as the problem does not fit into the cache,
01189         // use the LIBSVM 2.8 algorithm afterwards
01190         if (active * active > quadratic.getMaxCacheSize())
01191             currentWSS = &QpSvmDecomp::HMG;
01192         else
01193             currentWSS = &QpSvmDecomp::Libsvm28;
01194     }
01195 }
01196 
01197 void QpSvmDecomp::Shrink()
01198 {
01199     double largestUp = -1e100;
01200     double smallestDown = 1e100;
01201     std::vector<unsigned int> shrinked;
01202     unsigned int a;
01203     double v, g;
01204 
01205     for (a = 0; a < active; a++)
01206     {
01207         v = alpha(a);
01208         g = gradient(a);
01209         if (v > boxMin(a))
01210         {
01211             if (g < smallestDown) smallestDown = g;
01212         }
01213         if (v < boxMax(a))
01214         {
01215             if (g > largestUp) largestUp = g;
01216         }
01217     }
01218 
01219     if (! bUnshrinked && (largestUp - smallestDown < 10.0 * epsilon))
01220     {
01221         // unshrink the problem at this accuracy level
01222         if (printInfo) cout << "#" << flush;
01223         Unshrink();
01224         bUnshrinked = true;
01225         SelectWSS();
01226         return;
01227     }
01228 
01229     // identify the variables to shrink
01230     for (a = 0; a < active; a++)
01231     {
01232         if (a == old_i) continue;
01233         if (a == old_j) continue;
01234         v = alpha(a);
01235         g = gradient(a);
01236 
01237         if (v == boxMin(a))
01238         {
01239             if (g > smallestDown) continue;
01240         }
01241         else if (v == boxMax(a))
01242         {
01243             if (g < largestUp) continue;
01244         }
01245         else continue;
01246 
01247         // In this moment no feasible step including this variable
01248         // can improve the objective. Thus deactivate the variable.
01249         shrinked.push_back(a);
01250         if (quadratic.getCacheRowSize(a) > 0) quadratic.CacheRowRelease(a);
01251     }
01252 
01253     int s, sc = shrinked.size();
01254     if (sc == 0)
01255     {
01256         return;
01257     }
01258     unsigned int new_active = active - sc;
01259 
01260     // exchange variables such that shrinked variables
01261     // are moved to the ends of the lists.
01262     unsigned int k, high = active;
01263     for (s = sc - 1; s >= 0; s--)
01264     {
01265         k = shrinked[s];
01266         high--;
01267 
01268         // exchange the variables "k" and "high"
01269         FlipCoordinates(k, high);
01270     }
01271 
01272     // shrink the cache entries
01273     for (a = 0; a < active; a++)
01274     {
01275         if (quadratic.getCacheRowSize(a) > new_active) quadratic.CacheRowResize(a, new_active);
01276     }
01277 
01278     active = new_active;
01279 
01280     SelectWSS();
01281 }
01282 
01283 void QpSvmDecomp::Unshrink(bool complete)
01284 {
01285     if (printInfo) cout << "[" << flush;
01286     if (active == dimension)
01287     {
01288         if (printInfo) cout << "]" << flush;
01289         return;
01290     }
01291 
01292     unsigned int i, a;
01293     float* q;
01294     double v, g;
01295     double largestUp = -1e100;
01296     double smallestDown = 1e100;
01297 
01298     // compute the inactive gradient components (quadratic time complexity)
01299     for (a = active; a < dimension; a++) gradient(a) = linear(a);
01300     for (i = 0; i < dimension; i++)
01301     {
01302         v = alpha(i);
01303         if (v == 0.0) continue;
01304 
01305         q = quadratic.Row(i, active, dimension, true);
01306         for (a = active; a < dimension; a++) gradient(a) -= q[a] * v;
01307     }
01308 
01309     if (complete)
01310     {
01311         active = dimension;
01312         if (printInfo) cout << "]" << flush;
01313         return;
01314     }
01315 
01316     // find largest KKT violations
01317     for (a = 0; a < dimension; a++)
01318     {
01319         g = gradient(a);
01320         v = alpha(a);
01321 
01322         if (v > boxMin(a) && g < smallestDown) smallestDown = g;
01323         if (v < boxMax(a) && g > largestUp) largestUp = g;
01324     }
01325 
01326     // identify the variables to activate
01327     for (a = active; a < dimension; a++)
01328     {
01329         if (a == old_i) continue;
01330         if (a == old_j) continue;
01331         g = gradient(a);
01332         v = alpha(a);
01333 
01334         if (v == boxMin(a))
01335         {
01336             if (g <= smallestDown) continue;
01337         }
01338         else if (v == boxMax(a))
01339         {
01340             if (g >= largestUp) continue;
01341         }
01342 
01343         FlipCoordinates(active, a);
01344         active++;
01345     }
01346 
01347     if (printInfo) cout << active << "]" << flush;
01348 }
01349 
01350 void QpSvmDecomp::FlipCoordinates(unsigned int i, unsigned int j)
01351 {
01352     if (i == j) return;
01353 
01354     // check the previous working set
01355     if (old_i == i) old_i = j;
01356     else if (old_i == j) old_i = i;
01357 
01358     if (old_j == i) old_j = j;
01359     else if (old_j == j) old_j = i;
01360 
01361     // exchange entries in the simple lists
01362     XCHG_A(double, boxMin, i, j);
01363     XCHG_A(double, boxMax, i, j);
01364     XCHG_A(double, linear, i, j);
01365     XCHG_A(double, alpha, i, j);
01366     XCHG_A(unsigned int, permutation, i, j);
01367     XCHG_A(double, diagonal, i, j);
01368     XCHG_A(double, gradient, i, j);
01369 
01370     // notify the matrix cache
01371     quadratic.FlipColumnsAndRows(i, j);
01372 }
01373 
01374 
01376 
01377 
01378 QpBoxDecomp::QpBoxDecomp(CachedMatrix& quadraticPart)
01379 : quadratic(quadraticPart)
01380 {
01381     dimension = quadratic.getMatrixSize();
01382 
01383     // prepare lists
01384     alpha.resize(dimension, false);
01385     diagonal.resize(dimension, false);
01386     permutation.resize(dimension, false);
01387     gradient.resize(dimension, false);
01388     linear.resize(dimension, false);
01389     boxMin.resize(dimension, false);
01390     boxMax.resize(dimension, false);
01391 
01392     // prepare the permutation and the diagonal
01393     unsigned int i;
01394     for (i = 0; i < dimension; i++)
01395     {
01396         permutation(i) = i;
01397         diagonal(i) = quadratic.Entry(i, i);
01398     }
01399 
01400     maxIter = -1;
01401 }
01402 
01403 QpBoxDecomp::~QpBoxDecomp()
01404 {
01405 }
01406 
01407 
01408 void QpBoxDecomp::Solve(const Array<double>& linearPart,
01409                                const Array<double>& boxLower,
01410                                const Array<double>& boxUpper,
01411                                Array<double>& solutionVector,
01412                                double eps)
01413 {
01414     SIZE_CHECK(linearPart.ndim() == 1);
01415     SIZE_CHECK(boxLower.ndim() == 1);
01416     SIZE_CHECK(boxUpper.ndim() == 1);
01417     SIZE_CHECK(linearPart.dim(0) == dimension);
01418     SIZE_CHECK(boxLower.dim(0) == dimension);
01419     SIZE_CHECK(boxUpper.dim(0) == dimension);
01420 
01421     unsigned int a, i, j;
01422     float* q;
01423 
01424     for (i = 0; i < dimension; i++)
01425     {
01426         j = permutation(i);
01427         alpha(i) = solutionVector(j);
01428         linear(i) = linearPart(j);
01429         boxMin(i) = boxLower(j);
01430         boxMax(i) = boxUpper(j);
01431     }
01432 
01433     epsilon = eps;
01434 
01435     // prepare the solver internal variables
01436     active = dimension;
01437     gradient = linear;
01438 
01439     for (i = 0; i < dimension; i++)
01440     {
01441         if (boxMax(i) < boxMin(i)) throw SHARKEXCEPTION("[QpBoxDecomp::Solve] The feasible region is empty.");
01442 
01443         double v = alpha(i);
01444         if (v != 0.0)
01445         {
01446             q = quadratic.Row(i, 0, dimension);
01447             for (a = 0; a < dimension; a++) gradient(a) -= q[a] * v;
01448         }
01449     }
01450 
01451     // initial shrinking, e.g., for dummy variables
01452     Shrink();
01453 
01454     // decomposition loop
01455     SMO();
01456 
01457     // return alpha
01458     for (i = 0; i < dimension; i++)
01459     {
01460         solutionVector(permutation(i)) = alpha(i);
01461     }
01462 }
01463 
01464 void QpBoxDecomp::Continue(const Array<double>& boxLower,
01465                                const Array<double>& boxUpper,
01466                                Array<double>& solutionVector)
01467 {
01468     SIZE_CHECK(boxLower.ndim() == 1);
01469     SIZE_CHECK(boxUpper.ndim() == 1);
01470     SIZE_CHECK(boxLower.dim(0) == dimension);
01471     SIZE_CHECK(boxUpper.dim(0) == dimension);
01472     solutionVector.resize(alpha.dim(0), false);
01473 
01474     unsigned int i, j;
01475 
01476     for (i = 0; i < dimension; i++)
01477     {
01478         j = permutation(i);
01479         boxMin(i) = boxLower(j);
01480         boxMax(i) = boxUpper(j);
01481     }
01482 
01483     // initial shrinking
01484     Shrink();
01485 
01486     // decomposition loop
01487     SMO();
01488 
01489     // return alpha
01490     for (i = 0; i < dimension; i++)
01491     {
01492         solutionVector(permutation(i)) = alpha(i);
01493     }
01494 }
01495 
01496 void QpBoxDecomp::Continue(const Array<double>& gradientUpdate,
01497                                Array<double>& solutionVector)
01498 {
01499     SIZE_CHECK(gradientUpdate.ndim() == 1);
01500     SIZE_CHECK(gradientUpdate.dim(0) == dimension);
01501     solutionVector.resize(alpha.dim(0), false);
01502 
01503     unsigned int i;
01504 
01505     // update the gradient
01506     for (i=0; i<dimension; i++) gradient(i) = gradientUpdate(permutation(i));
01507 
01508     // re-compute active set
01509     Unshrink(false);
01510 
01511     // decomposition loop
01512     SMO();
01513 
01514     // return alpha
01515     for (i=0; i < dimension; i++) solutionVector(permutation(i)) = alpha(i);
01516 }
01517 
01518 void QpBoxDecomp::SMO()
01519 {
01520     unsigned int a, i;
01521     float* q;
01522 
01523     bUnshrinked = false;
01524     unsigned int shrinkCounter = (active < 1000) ? active : 1000;
01525 
01526     // decomposition loop
01527     iter = 0;
01528     optimal = false;
01529     while (iter != maxIter)
01530     {
01531         // select a working set and check for optimality
01532         if (SelectWorkingSet(i))
01533         {
01534             // seems to be optimal
01535 
01536             // do costly unshrinking
01537             Unshrink(true);
01538 
01539             // check again on the whole problem
01540             if (SelectWorkingSet(i))
01541             {
01542                 optimal = true;
01543                 break;
01544             }
01545 
01546             // shrink again
01547             Shrink();
01548             shrinkCounter = (active < 1000) ? active : 1000;
01549         }
01550 
01551         // SMO update
01552         {
01553             double ai = alpha(i);
01554             double Li = boxMin(i);
01555             double Ui = boxMax(i);
01556 
01557             // update alpha, that is, solve the sub-problem defined by i
01558             double nominator = gradient(i);
01559             double denominator = diagonal(i);
01560             double mu = nominator / denominator;
01561             if (ai + mu < Li) mu = Li - ai;
01562             else if (ai + mu > Ui) mu = Ui - ai;
01563             alpha(i) += mu;
01564 
01565             // get the matrix rows corresponding to the working set
01566             q = quadratic.Row(i, 0, active);
01567 
01568             // update the gradient
01569             for (a = 0; a < active; a++) gradient(a) -= mu * q[a];
01570         }
01571 
01572         shrinkCounter--;
01573         if (shrinkCounter == 0)
01574         {
01575             // shrink the problem
01576             Shrink();
01577 
01578             shrinkCounter = (active < 1000) ? active : 1000;
01579         }
01580 
01581         iter++;
01582     }
01583 }
01584 
01585 bool QpBoxDecomp::SelectWorkingSet(unsigned int& i)
01586 {
01587     double largest = 0.0;
01588     unsigned int a;
01589 
01590     for (a = 0; a < active; a++)
01591     {
01592         double v = alpha(a);
01593         double g = gradient(a);
01594         if (v < boxMax(a))
01595         {
01596             if (g > largest)
01597             {
01598                 largest = g;
01599                 i = a;
01600             }
01601         }
01602         if (v > boxMin(a))
01603         {
01604             if (-g > largest)
01605             {
01606                 largest = -g;
01607                 i = a;
01608             }
01609         }
01610     }
01611 
01612     // KKT stopping condition
01613     return (largest < epsilon);
01614 }
01615 
01616 void QpBoxDecomp::Shrink()
01617 {
01618     std::vector<unsigned int> shrinked;
01619     unsigned int a;
01620     double v, g;
01621 
01622     if (! bUnshrinked)
01623     {
01624         double largest = 0.0;
01625         for (a = 0; a < active; a++)
01626         {
01627             if (alpha(a) < boxMax(a))
01628             {
01629                 if (gradient(a) > largest)
01630                 {
01631                     largest = gradient(a);
01632                 }
01633             }
01634             if (alpha(a) > boxMin(a))
01635             {
01636                 if (-gradient(a) > largest)
01637                 {
01638                     largest = -gradient(a);
01639                 }
01640             }
01641         }
01642 
01643         if (largest < 10.0 * epsilon)
01644         {
01645             // unshrink the problem at this accuracy level
01646             Unshrink(true);
01647             bUnshrinked = true;
01648         }
01649     }
01650 
01651     // identify the variables to shrink
01652     for (a = 0; a < active; a++)
01653     {
01654         v = alpha(a);
01655         g = gradient(a);
01656 
01657         if ((v == boxMin(a) && g <= 0.0) || (v == boxMax(a) && g >= 0.0))
01658         {
01659             // In this moment no feasible step including this variable
01660             // can improve the objective. Thus deactivate the variable.
01661             shrinked.push_back(a);
01662             if (quadratic.getCacheRowSize(a) > 0) quadratic.CacheRowRelease(a);
01663         }
01664     }
01665 
01666     int s, sc = shrinked.size();
01667     if (sc == 0) return;
01668 
01669     unsigned int new_active = active - sc;
01670 
01671     // exchange variables such that shrinked variables
01672     // are moved to the ends of the lists.
01673     unsigned int k, high = active;
01674     for (s = sc - 1; s >= 0; s--)
01675     {
01676         k = shrinked[s];
01677         high--;
01678 
01679         // exchange the variables "k" and "high"
01680         FlipCoordinates(k, high);
01681     }
01682 
01683     // shrink the cache entries
01684     for (a = 0; a < active; a++)
01685     {
01686         if (quadratic.getCacheRowSize(a) > new_active) quadratic.CacheRowResize(a, new_active);
01687     }
01688 
01689     active = new_active;
01690 }
01691 
01692 void QpBoxDecomp::Unshrink(bool complete)
01693 {
01694     if (active == dimension) return;
01695 
01696     unsigned int i, a;
01697     float* q;
01698     double v;
01699 
01700     // compute the inactive gradient components (quadratic time complexity)
01701     for (a = active; a < dimension; a++) gradient(a) = linear(a);
01702     for (i = 0; i < dimension; i++)
01703     {
01704         v = alpha(i);
01705         if (v == 0.0) continue;
01706 
01707         q = quadratic.Row(i, active, dimension, true);
01708         for (a = active; a < dimension; a++) gradient(a) -= q[a] * v;
01709     }
01710 
01711     active = dimension;
01712     if (! complete) Shrink();
01713 }
01714 
01715 void QpBoxDecomp::FlipCoordinates(unsigned int i, unsigned int j)
01716 {
01717     if (i == j) return;
01718 
01719     // exchange entries in the simple lists
01720     XCHG_A(double, boxMin, i, j);
01721     XCHG_A(double, boxMax, i, j);
01722     XCHG_A(double, linear, i, j);
01723     XCHG_A(double, alpha, i, j);
01724     XCHG_A(unsigned int, permutation, i, j);
01725     XCHG_A(double, diagonal, i, j);
01726     XCHG_A(double, gradient, i, j);
01727 
01728     // notify the matrix cache
01729     quadratic.FlipColumnsAndRows(i, j);
01730 }
01731 
01732 
01734 
01735 
01736 QpBoxAllInOneDecomp::QpBoxAllInOneDecomp(CachedMatrix& kernel)
01737 : kernelMatrix(kernel)
01738 {
01739     examples = kernelMatrix.getMatrixSize();
01740     maxIter = -1;
01741 }
01742 
01743 QpBoxAllInOneDecomp::~QpBoxAllInOneDecomp()
01744 {
01745 }
01746 
01747 
01748 void QpBoxAllInOneDecomp::Solve(unsigned int classes,
01749                 const Array<double>& target,
01750                 const Array<double>& prototypes,
01751                 double C,
01752                 Array<double>& solutionVector,
01753                 double eps)
01754 {
01755     SIZE_CHECK(target.ndim() == 2);
01756 
01757     this->classes = classes;
01758     variables = examples * classes;
01759 
01760     SIZE_CHECK(target.dim(0) == examples);
01761     SIZE_CHECK(target.dim(1) == 1);
01762 
01763     unsigned int a, b, bc, i, j;
01764     float* q = NULL;
01765 
01766     // precompute inner products of prototypes
01767     // and lengths of differences of prototypes
01768     m_prototypeProduct.resize(classes, classes, false);
01769     m_prototypeLength.resize(classes, classes, false);
01770     for (a=0; a<classes; a++)
01771     {
01772         for (b=0; b<=a; b++)
01773         {
01774             m_prototypeProduct(a, b) = m_prototypeProduct(b, a)
01775                 = scalarProduct(prototypes[a], prototypes[b]);
01776             Array<double> diff = prototypes[a] - prototypes[b];
01777             m_prototypeLength(a, b) = m_prototypeLength(b, a)
01778                 = sqrt(scalarProduct(diff, diff));
01779         }
01780     }
01781 
01782     // prepare lists
01783     alpha.resize(variables, false);
01784     diagonal.resize(examples, false);
01785     gradient.resize(variables, false);
01786     linear.resize(variables, false);
01787     boxMin.resize(variables, false);
01788     boxMax.resize(variables, false);
01789     example.resize(examples);
01790     variable.resize(variables);
01791     storage.resize(variables);
01792 
01793     // prepare list contents
01794     for (i = 0; i < examples; i++)
01795     {
01796         diagonal(i) = kernelMatrix.Entry(i, i);
01797         example[i].index = i;
01798         example[i].label = (unsigned int)target(i, 0);
01799         example[i].active = classes;
01800         example[i].variables = &storage[classes * i];
01801     }
01802     for (i = 0; i < variables; i++)
01803     {
01804         variable[i].example = i / classes;
01805         variable[i].index = i % classes;
01806         variable[i].label = i % classes;
01807         storage[i] = i;
01808     }
01809 
01810     // prepare the solver internal variables
01811     for (a = 0; a < examples; a++)
01812     {
01813         unsigned int y = example[a].label;
01814         for (b = 0; b < classes; b++)
01815         {
01816             j = example[a].variables[b];
01817             unsigned int v = variable[j].label;
01818             alpha(j) = solutionVector(j);
01819             linear(j) = 1.0;
01820             boxMin(j) = 0.0;
01821             if (y == v) boxMax(j) = 0.0;
01822             else boxMax(j) = C;
01823         }
01824     }
01825     gradient = linear;
01826 
01827     epsilon = eps;
01828 
01829     activeEx = examples;
01830     activeVar = variables;
01831 
01832     unsigned int e = 0xffffffff;
01833     for (i = 0; i < variables; i++)
01834     {
01835         if (boxMax(i) < boxMin(i)) throw SHARKEXCEPTION("[QpBoxAllInOneDecomp::Solve] The feasible region is empty.");
01836 
01837         double v = alpha(i);
01838         if (v != 0.0)
01839         {
01840             unsigned int ei = variable[i].example;
01841             unsigned int vi = variable[i].label;
01842             unsigned int yi = example[ei].label;
01843             if (ei != e)
01844             {
01845                 q = kernelMatrix.Row(ei, 0, activeEx);
01846                 e = ei;
01847             }
01848 
01849             for (a = 0; a < activeEx; a++)
01850             {
01851                 double k = q[a];
01852                 tExample* ex = &example[a];
01853                 bc = ex->active;
01854                 for (b = 0; b < bc; b++)
01855                 {
01856                     j = ex->variables[b];
01857                     unsigned int vj = variable[j].label;
01858                     unsigned int yj = ex->label;
01859                     double km = Modifier(vi, vj, yi, yj);
01860                     gradient(j) -= v * km * k;
01861                 }
01862             }
01863         }
01864     }
01865 
01866     // initial shrinking (useful for dummy variables and warm starts)
01867     Shrink();
01868 
01869     bUnshrinked = false;
01870     unsigned int shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
01871 
01872     // decomposition loop
01873     iter = 0;
01874     optimal = false;
01875     while (iter != maxIter)
01876     {
01877         // select a working set and check for optimality
01878         if (SelectWorkingSet(i))
01879         {
01880             // seems to be optimal
01881 
01882             // do costly unshrinking
01883             Unshrink(true);
01884 
01885             // check again on the whole problem
01886             if (SelectWorkingSet(i))
01887             {
01888                 optimal = true;
01889                 break;
01890             }
01891 
01892             // shrink again
01893             Shrink();
01894             shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
01895         }
01896 
01897         // update
01898         {
01899             double ai = alpha(i);
01900             double Li = boxMin(i);
01901             double Ui = boxMax(i);
01902             unsigned int ei = variable[i].example;
01903             unsigned int vi = variable[i].label;
01904             unsigned int yi = example[ei].label;
01905 
01906             // update alpha, that is, solve the sub-problem defined by i
01907             double km = Modifier(vi, vi, yi, yi);
01908             double nominator = gradient(i);
01909             double denominator = km * diagonal(ei);
01910             double mu = nominator / denominator;
01911             if (ai + mu < Li) mu = Li - ai;
01912             else if (ai + mu > Ui) mu = Ui - ai;
01913             alpha(i) += mu;
01914 
01915             // get the matrix row corresponding to the working set
01916             q = kernelMatrix.Row(ei, 0, activeEx);
01917 
01918             // update the gradient
01919             for (a = 0; a < activeEx; a++)
01920             {
01921                 double k = q[a];
01922                 tExample* ex = &example[a];
01923                 bc = ex->active;
01924                 for (b = 0; b < bc; b++)
01925                 {
01926                     j = ex->variables[b];
01927 
01928                     unsigned int vj = variable[j].label;
01929                     unsigned int yj = ex->label;
01930                     double km = Modifier(vi, vj, yi, yj);
01931                     gradient(j) -= mu * km * k;
01932                 }
01933             }
01934         }
01935 
01936         shrinkCounter--;
01937         if (shrinkCounter == 0)
01938         {
01939             // shrink the problem
01940             Shrink();
01941 
01942             shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
01943         }
01944 
01945         iter++;
01946     }
01947 
01948     // return alpha
01949     for (i = 0; i < variables; i++)
01950     {
01951         unsigned int j = classes * example[variable[i].example].index + variable[i].label;
01952         solutionVector(j) = alpha(i);
01953     }
01954 }
01955 
01956 void QpBoxAllInOneDecomp::Continue(double largerC, Array<double>& solutionVector)
01957 {
01958     unsigned int a, b, bc, i = 0, j;
01959     float* q = NULL;
01960 
01961     solutionVector.resize(variables, false);
01962 
01963     // set the new box size
01964     for (a = 0; a < variables; a++)
01965     {
01966         if (boxMax(a) > 0.0) boxMax(a) = largerC;
01967     }
01968 
01969     // initial shrinking (useful for dummy variables
01970     // AND recent non-support-vectors)
01971     // Shrink();
01972 
01973     bUnshrinked = false;
01974     unsigned int shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
01975 
01976     // decomposition loop
01977     while (true)
01978     {
01979         // select a working set and check for optimality
01980         if (SelectWorkingSet(i))
01981         {
01982             // seems to be optimal
01983 
01984             // do costly unshrinking
01985             Unshrink(true);
01986 
01987             // check again on the whole problem
01988             if (SelectWorkingSet(i)) break;
01989 
01990             // shrink again
01991             Shrink();
01992             shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
01993         }
01994 
01995         // update
01996         {
01997             double ai = alpha(i);
01998             double Li = boxMin(i);
01999             double Ui = boxMax(i);
02000             unsigned int ei = variable[i].example;
02001             unsigned int vi = variable[i].label;
02002             unsigned int yi = example[ei].label;
02003 
02004             // update alpha, that is, solve the sub-problem defined by i
02005             double km = Modifier(vi, vi, yi, yi);
02006             double nominator = gradient(i);
02007             double denominator = km * diagonal(ei);
02008             double mu = nominator / denominator;
02009             if (ai + mu < Li) mu = Li - ai;
02010             else if (ai + mu > Ui) mu = Ui - ai;
02011             alpha(i) += mu;
02012 
02013             // get the matrix row corresponding to the working set
02014             q = kernelMatrix.Row(ei, 0, activeEx);
02015 
02016             // update the gradient
02017             for (a = 0; a < activeEx; a++)
02018             {
02019                 double k = q[a];
02020                 tExample* ex = &example[a];
02021                 bc = ex->active;
02022                 for (b = 0; b < bc; b++)
02023                 {
02024                     j = ex->variables[b];
02025 
02026                     unsigned int vj = variable[j].label;
02027                     unsigned int yj = ex->label;
02028                     double km = Modifier(vi, vj, yi, yj);
02029                     gradient(j) -= mu * km * k;
02030                 }
02031             }
02032         }
02033 
02034         shrinkCounter--;
02035         if (shrinkCounter == 0)
02036         {
02037             // shrink the problem
02038             Shrink();
02039 
02040             shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
02041         }
02042 
02043         iter++;
02044     }
02045 
02046     // return alpha
02047     for (i = 0; i < variables; i++)
02048     {
02049         unsigned int j = classes * example[variable[i].example].index + variable[i].label;
02050         solutionVector(j) = alpha(i);
02051     }
02052 }
02053 
02054 double QpBoxAllInOneDecomp::Modifier(unsigned int v_i, unsigned int v_j, unsigned int y_i, unsigned int y_j)
02055 {
02056     double p = m_prototypeProduct(y_i, y_j) - m_prototypeProduct(v_i, y_j) - m_prototypeProduct(y_i, v_j) + m_prototypeProduct(v_i, v_j);
02057     // check for zero entry in sparse matrix
02058     if (p == 0.0) return 0.0;
02059     p /= (m_prototypeLength(y_i, v_i) * m_prototypeLength(y_j, v_j));
02060     return p;
02061 }
02062 
02063 bool QpBoxAllInOneDecomp::SelectWorkingSet(unsigned int& i)
02064 {
02065     double largest = 0.0;
02066     unsigned int a;
02067 
02068     for (a = 0; a < activeVar; a++)
02069     {
02070         double v = alpha(a);
02071         double g = gradient(a);
02072         if (v < boxMax(a))
02073         {
02074             if (g > largest)
02075             {
02076                 largest = g;
02077                 i = a;
02078             }
02079         }
02080         if (v > boxMin(a))
02081         {
02082             if (-g > largest)
02083             {
02084                 largest = -g;
02085                 i = a;
02086             }
02087         }
02088     }
02089 
02090     // KKT stopping condition
02091     return (largest < epsilon);
02092 }
02093 
02094 void QpBoxAllInOneDecomp::Shrink()
02095 {
02096     int a;
02097     double v, g;
02098 
02099     if (! bUnshrinked)
02100     {
02101         double largest = 0.0;
02102         for (a = 0; a < (int)activeVar; a++)
02103         {
02104             if (alpha(a) < boxMax(a))
02105             {
02106                 if (gradient(a) > largest) largest = gradient(a);
02107             }
02108             if (alpha(a) > boxMin(a))
02109             {
02110                 if (-gradient(a) > largest) largest = -gradient(a);
02111             }
02112         }
02113         if (largest < 10.0 * epsilon)
02114         {
02115             // unshrink the problem at this accuracy level
02116             Unshrink(true);
02117             bUnshrinked = true;
02118         }
02119     }
02120 
02121     // shrink variables
02122     bool se = false;
02123     for (a = activeVar-1; a >= 0; a--)
02124     {
02125         v = alpha(a);
02126         g = gradient(a);
02127 
02128         if ((v == boxMin(a) && g <= 0.0) || (v == boxMax(a) && g >= 0.0))
02129         {
02130             // In this moment no feasible step including this variable
02131             // can improve the objective. Thus deactivate the variable.
02132             unsigned int e = variable[a].example;
02133             DeactivateVariable(a);
02134             if (example[e].active == 0)
02135             {
02136                 se = true;
02137             }
02138         }
02139     }
02140 
02141     if (se)
02142     {
02143         // exchange examples such that shrinked examples
02144         // are moved to the ends of the lists
02145         for (a = activeEx-1; a >= 0; a--)
02146         {
02147             if (example[a].active == 0) DeactivateExample(a);
02148         }
02149 
02150         // shrink the corresponding cache entries
02151         for (a = 0; a < (int)activeEx; a++)
02152         {
02153             if (kernelMatrix.getCacheRowSize(a) > activeEx) kernelMatrix.CacheRowResize(a, activeEx);
02154         }
02155     }
02156 }
02157 
02158 void QpBoxAllInOneDecomp::Unshrink(bool complete)
02159 {
02160     if (activeVar == variables) return;
02161 
02162     unsigned int i, a;
02163     float* q;
02164     double v;
02165 
02166     // compute the inactive gradient components (quadratic time complexity)
02167     for (a = activeVar; a < variables; a++) gradient(a) = linear(a);
02168     for (i = 0; i < variables; i++)
02169     {
02170         v = alpha(i);
02171         if (v == 0.0) continue;
02172 
02173         unsigned int ei = variable[i].example;
02174         unsigned int vi = variable[i].label;
02175         unsigned int yi = example[ei].label;
02176         q = kernelMatrix.Row(ei, 0, examples, true);
02177         for (a = activeVar; a < variables; a++)
02178         {
02179             unsigned int ea = variable[a].example;
02180             unsigned int va = variable[a].label;
02181             unsigned int ya = example[ea].label;
02182 
02183             double km = Modifier(vi, va, yi, ya);
02184             double k = q[ea];
02185             gradient(a) -= km * k * v;
02186         }
02187     }
02188 
02189     for (i = 0; i < examples; i++) example[i].active = classes;
02190     activeEx = examples;
02191     activeVar = variables;
02192 
02193     if (! complete) Shrink();
02194 }
02195 
02196 void QpBoxAllInOneDecomp::DeactivateVariable(unsigned int v)
02197 {
02198     unsigned int ev = variable[v].example;
02199     unsigned int iv = variable[v].index;
02200     tExample* exv = &example[ev];
02201     unsigned int ih = exv->active - 1;
02202     unsigned int h = exv->variables[ih];
02203     variable[v].index = ih;
02204     variable[h].index = iv;
02205     std::swap(exv->variables[iv], exv->variables[ih]);
02206     iv = ih;
02207     exv->active--;
02208 
02209     unsigned int j = activeVar - 1;
02210     unsigned int ej = variable[j].example;
02211     unsigned int ij = variable[j].index;
02212     tExample* exj = &example[ej];
02213 
02214     // exchange entries in the lists
02215     XCHG_A(double, boxMin, v, j);
02216     XCHG_A(double, boxMax, v, j);
02217     XCHG_A(double, linear, v, j);
02218     XCHG_A(double, alpha, v, j);
02219     XCHG_A(double, gradient, v, j);
02220     XCHG_V(tVariable, variable, v, j);
02221 
02222     std::swap(exv->variables[iv], exj->variables[ij]);
02223 
02224     activeVar--;
02225 }
02226 
02227 void QpBoxAllInOneDecomp::DeactivateExample(unsigned int e)
02228 {
02229     unsigned int j = activeEx - 1;
02230 
02231     XCHG_A(double, diagonal, e, j);
02232     XCHG_V(tExample, example, e, j);
02233 
02234     unsigned int v;
02235     unsigned int* pe = example[e].variables;
02236     unsigned int* pj = example[j].variables;
02237     for (v=0; v<classes; v++)
02238     {
02239         variable[pe[v]].example = e;
02240         variable[pj[v]].example = j;
02241     }
02242 
02243     // notify the matrix cache
02244     kernelMatrix.CacheRowRelease(e);
02245     kernelMatrix.FlipColumnsAndRows(e, j);
02246 
02247     activeEx--;
02248 }
02249 
02250 
02252 
02253 
02254 QpCrammerSingerDecomp::QpCrammerSingerDecomp(CachedMatrix& kernel, const Array<double>& y, unsigned int classes)
02255 : kernelMatrix(kernel)
02256 {
02257     SIZE_CHECK(y.ndim() == 2);
02258 
02259     examples = kernelMatrix.getMatrixSize();
02260     this->classes = classes;
02261     variables = examples * classes;
02262 
02263     SIZE_CHECK(y.dim(0) == examples);
02264     SIZE_CHECK(y.dim(1) == 1);
02265 
02266     // prepare lists
02267     alpha.resize(variables, false);
02268     diagonal.resize(examples, false);
02269     gradient.resize(variables, false);
02270     linear.resize(variables, false);
02271     boxMin.resize(variables, false);
02272     boxMax.resize(variables, false);
02273     example.resize(examples);
02274     variable.resize(variables);
02275     storage.resize(variables);
02276 
02277     // prepare the lists
02278     unsigned int i;
02279     for (i = 0; i < examples; i++)
02280     {
02281         diagonal(i) = kernelMatrix.Entry(i, i);
02282         example[i].index = i;
02283         example[i].label = (unsigned int)y(i, 0);
02284         example[i].active = classes;
02285         example[i].variables = &storage[classes * i];
02286     }
02287     for (i = 0; i < variables; i++)
02288     {
02289         variable[i].example = i / classes;
02290         variable[i].index = i % classes;
02291         variable[i].label = i % classes;
02292         storage[i] = i;
02293     }
02294 
02295     maxIter = -1;
02296 }
02297 
02298 QpCrammerSingerDecomp::~QpCrammerSingerDecomp()
02299 {
02300 }
02301 
02302 
02303 void QpCrammerSingerDecomp::Solve(Array<double>& solutionVector,
02304                                     double beta,
02305                                     double eps)
02306 {
02307     unsigned int a, b, bc, i, j = 0;
02308     float* q = NULL;
02309 
02310     // prepare the solver internal variables
02311     for (a = 0; a < examples; a++)
02312     {
02313         unsigned int y = example[a].label;
02314         for (b = 0; b < classes; b++)
02315         {
02316             j = example[a].variables[b];
02317             alpha(j) = solutionVector(j);
02318             unsigned int v = variable[j].label;
02319             if (y == v)
02320             {
02321                 linear(j) = beta;
02322                 boxMin(j) = -1e100;
02323                 boxMax(j) = 1.0;
02324             }
02325             else
02326             {
02327                 linear(j) = 0.0;
02328                 boxMin(j) = -1e100;
02329                 boxMax(j) = 0.0;
02330             }
02331         }
02332     }
02333 
02334     epsilon = eps;
02335 
02336     activeEx = examples;
02337     activeVar = variables;
02338 
02339     // compute the initial gradient
02340     gradient = linear;
02341     unsigned int e = 0xffffffff;
02342     for (a=0; a<activeEx; a++)
02343     {
02344         tExample* ex = &example[a];
02345         bc = ex->active;
02346         for (b = 0; b < bc; b++)
02347         {
02348             j = ex->variables[b];
02349             double v = alpha(j);
02350             if (v != 0.0)
02351             {
02352                 unsigned int h;
02353                 unsigned int vj = variable[j].label;
02354                 if (e != a)
02355                 {
02356                     q = kernelMatrix.Row(a, 0, activeEx);
02357                     e = a;
02358                 }
02359                 for (h=0; h<activeVar; h++)
02360                 {
02361                     unsigned int vh = variable[h].label;
02362                     if (vh == vj)
02363                     {
02364                         unsigned int eh = variable[h].example;
02365                         gradient(h) -= v * q[eh];
02366                     }
02367                 }
02368             }
02369         }
02370     }
02371 
02372     // initial shrinking
02373     Shrink();
02374 
02375     bUnshrinked = false;
02376     unsigned int shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
02377 
02378     // decomposition loop
02379     iter = 0;
02380     optimal = false;
02381     while (iter != maxIter)
02382     {
02383         // select a working set and check for optimality
02384         if (SelectWorkingSet(i, j) <= epsilon)
02385         {
02386             // seems to be optimal
02387 
02388             // do costly unshrinking
02389             Unshrink(true);
02390 
02391             // check again on the whole problem
02392             if (SelectWorkingSet(i, j) <= epsilon)
02393             {
02394                 optimal = true;
02395                 break;
02396             }
02397 
02398             // shrink again
02399             Shrink();
02400             shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
02401         }
02402 
02403         // SMO update
02404         {
02405             // there is only one example corresponding to both variables:
02406             unsigned int e = variable[i].example;
02407 
02408             double ai = alpha(i);
02409             double Li = boxMin(i);
02410             double Ui = boxMax(i);
02411             double aj = alpha(j);
02412             double Lj = boxMin(j);
02413             double Uj = boxMax(j);
02414 
02415             unsigned int vi = variable[i].label;        // these are
02416             unsigned int vj = variable[j].label;        // different!
02417 
02418             // get the matrix rows corresponding to the working set
02419             q = kernelMatrix.Row(e, 0, activeEx);
02420 
02421             // update alpha, that is, solve the sub-problem defined by i and j
02422             double nominator = gradient(i) - gradient(j);
02423             double denominator = 2.0 * diagonal(e);
02424             double mu = nominator / denominator;
02425             if (ai + mu < Li) mu = Li - ai;
02426             else if (ai + mu > Ui) mu = Ui - ai;
02427             if (aj - mu < Lj) mu = aj - Lj;
02428             else if (aj - mu > Uj) mu = aj - Uj;
02429             alpha(i) += mu;
02430             alpha(j) -= mu;
02431 
02432             // update the gradient
02433             for (a = 0; a < activeEx; a++)
02434             {
02435                 double k = q[a];
02436                 tExample* ex = &example[a];
02437                 unsigned int b, bc = ex->active;
02438                 for (b = 0; b < bc; b++)
02439                 {
02440                     unsigned int h = ex->variables[b];
02441                     unsigned int vh = variable[h].label;
02442                     if (vh == vi) gradient(h) -= mu * k;
02443                     else if (vh == vj) gradient(h) += mu * k;
02444                 }
02445             }
02446         }
02447 
02448         shrinkCounter--;
02449         if (shrinkCounter == 0)
02450         {
02451             // shrink the problem
02452             Shrink();
02453 
02454             shrinkCounter = (activeVar < 1000) ? activeVar : 1000;
02455         }
02456 
02457         iter++;
02458     }
02459 
02460     // return alpha
02461     for (i = 0; i < variables; i++)
02462     {
02463         unsigned int j = classes * example[variable[i].example].index + variable[i].label;
02464         solutionVector(j) = alpha(i);
02465     }
02466 }
02467 
02468 // Select a working set (i, j) composed of an "up"-component i and
02469 // a "down"-component j. Return the corresponding KKT violation.
02470 double QpCrammerSingerDecomp::SelectWorkingSet(unsigned int& i, unsigned int& j)
02471 {
02472     double largest = 0.0;
02473 
02474     unsigned int a, b, bc, w;
02475     for (a=0; a<activeEx; a++)
02476     {
02477         tExample* ex = &example[a];
02478         bc = ex->active;
02479         double m = 1e100;
02480         double M = -1e100;
02481         unsigned int n = 0, N = 1;
02482         for (b=0; b<bc; b++)
02483         {
02484             w = ex->variables[b];
02485             double v = alpha(w);
02486             double g = gradient(w);
02487             if (v < boxMax(w))
02488             {
02489                 if (g > M)
02490                 {
02491                     M = g;
02492                     N = w;
02493                 }
02494             }
02495             if (v > boxMin(w))
02496             {
02497                 if (g < m)
02498                 {
02499                     m = g;
02500                     n = w;
02501                 }
02502             }
02503         }
02504         if (M - m > largest)
02505         {
02506             largest = M - m;
02507             i = N;
02508             j = n;
02509         }
02510     }
02511 
02512     return largest;
02513 }
02514 
02515 void QpCrammerSingerDecomp::Shrink()
02516 {
02517     if (! bUnshrinked)
02518     {
02519         unsigned int i, j;
02520         double violation = SelectWorkingSet(i, j);
02521         if (violation < 10.0 * epsilon)
02522         {
02523             // unshrink the problem at this accuracy level
02524             Unshrink(true);
02525             bUnshrinked = true;
02526         }
02527     }
02528 
02529     // loop through the examples
02530     bool se = false;
02531     int a;
02532     for (a = activeEx-1; a >= 0; a--)
02533     {
02534         // loop through the variables corresponding to the example
02535         tExample* ex = &example[a];
02536         unsigned int b, bc = ex->active;
02537         double m = 1e100;
02538         double M = -1e100;
02539         for (b=0; b<bc; b++)
02540         {
02541             unsigned int w = ex->variables[b];
02542             double v = alpha(w);
02543             double g = gradient(w);
02544             if (v < boxMax(w))
02545             {
02546                 if (g > M)
02547                 {
02548                     M = g;
02549                 }
02550             }
02551             if (v > boxMin(w))
02552             {
02553                 if (g < m)
02554                 {
02555                     m = g;
02556                 }
02557             }
02558         }
02559         for (b=0; b<bc; b++)
02560         {
02561             unsigned int w = ex->variables[b];
02562             double v = alpha(w);
02563             double g = gradient(w);
02564             if (v == boxMin(w) && g < m)
02565             {
02566                 DeactivateVariable(w);
02567                 b--;
02568                 bc--;
02569             }
02570             else if (v == boxMax(w) && g > M)
02571             {
02572                 DeactivateVariable(w);
02573                 b--;
02574                 bc--;
02575             }
02576         }
02577         if (bc == 0)
02578         {
02579             DeactivateExample(a);
02580             se = true;
02581         }
02582     }
02583 
02584     if (se)
02585     {
02586         // shrink the corresponding cache entries
02587         for (a = 0; a < (int)activeEx; a++)
02588         {
02589             if (kernelMatrix.getCacheRowSize(a) > activeEx) kernelMatrix.CacheRowResize(a, activeEx);
02590         }
02591     }
02592 }
02593 
02594 void QpCrammerSingerDecomp::Unshrink(bool complete)
02595 {
02596     if (activeVar == variables) return;
02597 
02598     unsigned int i, a;
02599     float* q;
02600     double v;
02601 
02602     // compute the inactive gradient components (quadratic time complexity)
02603     for (a = activeVar; a < variables; a++) gradient(a) = linear(a);
02604     for (i = 0; i < variables; i++)
02605     {
02606         v = alpha(i);
02607         if (v == 0.0) continue;
02608 
02609         unsigned int ei = variable[i].example;
02610         unsigned int vi = variable[i].label;
02611         q = kernelMatrix.Row(ei, 0, examples, true);
02612         for (a = activeVar; a < variables; a++)
02613         {
02614             if (variable[a].label == vi)
02615             {
02616                 unsigned int ea = variable[a].example;
02617                 gradient(a) -= q[ea] * v;
02618             }
02619         }
02620     }
02621 
02622     for (i = 0; i < examples; i++) example[i].active = classes;
02623     activeEx = examples;
02624     activeVar = variables;
02625 
02626     if (! complete) Shrink();
02627 }
02628 
02629 void QpCrammerSingerDecomp::DeactivateVariable(unsigned int v)
02630 {
02631     unsigned int ev = variable[v].example;
02632     unsigned int iv = variable[v].index;
02633     tExample* exv = &example[ev];
02634     unsigned int ih = exv->active - 1;
02635     unsigned int h = exv->variables[ih];
02636     variable[v].index = ih;
02637     variable[h].index = iv;
02638     std::swap(exv->variables[iv], exv->variables[ih]);
02639     iv = ih;
02640     exv->active--;
02641 
02642     unsigned int j = activeVar - 1;
02643     unsigned int ej = variable[j].example;
02644     unsigned int ij = variable[j].index;
02645     tExample* exj = &example[ej];
02646 
02647     // exchange entries in the lists
02648     XCHG_A(double, boxMin, v, j);
02649     XCHG_A(double, boxMax, v, j);
02650     XCHG_A(double, linear, v, j);
02651     XCHG_A(double, alpha, v, j);
02652     XCHG_A(double, gradient, v, j);
02653     XCHG_V(tVariable, variable, v, j);
02654 
02655     std::swap(exv->variables[iv], exj->variables[ij]);
02656 
02657     activeVar--;
02658 }
02659 
02660 void QpCrammerSingerDecomp::DeactivateExample(unsigned int e)
02661 {
02662     unsigned int j = activeEx - 1;
02663 
02664     XCHG_A(double, diagonal, e, j);
02665     XCHG_V(tExample, example, e, j);
02666 
02667     unsigned int v;
02668     unsigned int* pe = example[e].variables;
02669     unsigned int* pj = example[j].variables;
02670     for (v=0; v<classes; v++)
02671     {
02672         variable[pe[v]].example = e;
02673         variable[pj[v]].example = j;
02674     }
02675 
02676     // notify the matrix cache
02677     kernelMatrix.CacheRowRelease(e);
02678     kernelMatrix.FlipColumnsAndRows(e, j);
02679 
02680     activeEx--;
02681 }
02682 
02683 
02685 
02686 
02687 void QpBoxAndEqCG::Solve(const Array<double>& quadratic,
02688         const Array<double>& linear,
02689         const Array<double>& boxMin,
02690         const Array<double>& boxMax,
02691         const Array<double>& eqMat,
02692         Array<double>& point,
02693         double accuracy)
02694 {
02695     SIZE_CHECK(quadratic.ndim() == 2);
02696     SIZE_CHECK(linear.ndim() == 1);
02697     SIZE_CHECK(boxMin.ndim() == 1);
02698     SIZE_CHECK(boxMax.ndim() == 1);
02699     SIZE_CHECK(eqMat.ndim() == 2);
02700     SIZE_CHECK(point.ndim() == 1);
02701 
02702     int dimension = quadratic.dim(0);
02703     int equations = eqMat.dim(0);
02704 
02705     SIZE_CHECK((int)quadratic.dim(1) == dimension);
02706     SIZE_CHECK((int)linear.dim(0) == dimension);
02707     SIZE_CHECK((int)boxMin.dim(0) == dimension);
02708     SIZE_CHECK((int)boxMax.dim(0) == dimension);
02709     SIZE_CHECK((int)eqMat.dim(1) == dimension);
02710     SIZE_CHECK((int)point.dim(0) == dimension);
02711 
02712     int i, j;
02713     Array<double> direction(dimension);
02714     Array<bool> active(dimension);
02715     Array<double> gradient(dimension);
02716     Array<double> conjugate(dimension);
02717     Array<double> equality(eqMat);
02718     conjugate = 0.0;
02719     double DirLen2 = 0.0;
02720     double DirLen2old = 0.0;
02721     double accuracy2 = accuracy * accuracy;
02722     bool changed, bounds_changed;
02723     int nActive = 0;
02724     int nUpdatesLeft = -1;
02725     int bound = -1;
02726     int nReset = 0;
02727     double step = 0.0;
02728     double clipped = 0.0;
02729     double value;
02730     double first = 0.0;
02731     double second = 0.0;
02732     double norm2;
02733 
02734     active = false;
02735 
02736     int iter, maxiter = 10 * dimension * dimension;
02737     for (iter=0; iter<maxiter; iter++)
02738     {
02739         if (nUpdatesLeft == -1)
02740         {
02741             // compute the gradient from scratch
02742             for (i=0; i<dimension; i++)
02743             {
02744                 value = linear(i);
02745                 for (j=0; j<dimension; j++) value += quadratic(i, j) * point(j);
02746                 gradient(i) = value;
02747             }
02748             nUpdatesLeft = 100;
02749         }
02750 
02751         // compute the set of active inequality constraints
02752         bounds_changed = false;
02753         do
02754         {
02755             changed = false;
02756             for (i=0; i<dimension; i++)
02757             {
02758                 if (active(i))
02759                 {
02760                     equality = eqMat;
02761                     active(i) = false;
02762                     Project(active, equality, gradient, direction);
02763                     double g = direction(i);
02764                     if ((g <= 0.0 && point(i) == boxMin(i))
02765                             || (g >= 0.0 && point(i) == boxMax(i)))
02766                     {
02767                         nActive--;
02768                         changed = true;
02769                     }
02770                     else active(i) = true;
02771                 }
02772                 else
02773                 {
02774                     equality = eqMat;
02775                     active(i) = true;
02776                     Project(active, equality, gradient, direction);
02777                     double g = direction(i);
02778                     if ((g > 0.0 && point(i) == boxMin(i))
02779                             || (g < 0.0 && point(i) == boxMax(i)))
02780                     {
02781                         nActive++;
02782                         changed = true;
02783                     }
02784                     else active(i) = false;
02785                 }
02786             }
02787             bounds_changed = bounds_changed || changed;
02788         }
02789         while (changed);
02790         if (bounds_changed && iter > 0) nReset = 0;
02791 
02792         // check corner condition
02793         if (nActive + 1 >= dimension) break;
02794 
02795         // compute the gradient of the sub problem
02796         // projected onto the equality constraint
02797         equality = eqMat;
02798         Project(active, equality, gradient, direction);
02799 
02800         // compute the length of the remaining vector
02801         DirLen2old = DirLen2;
02802         DirLen2 = 0.0;
02803         for (i=0; i<dimension; i++)
02804         {
02805             if (active(i)) continue;
02806             value = direction(i);
02807             DirLen2 += value * value;
02808         }
02809 
02810         // update the conjugate direction
02811         nReset--;
02812         if (nReset == 0)
02813         {
02814             // In theory we are done.
02815             // However, we may which to continue
02816             // in order to avoid numerical problems.
02817             break;
02818         }
02819         else if (nReset < 0)
02820         {
02821             // the set of active constraints has changed
02822             conjugate = direction;
02823             nReset = dimension - equations - nActive;
02824         }
02825         else
02826         {
02827             double beta = DirLen2 / DirLen2old;
02828             for (i=0; i<dimension; i++)
02829             {
02830                 if (active(i)) continue;
02831                 else conjugate(i) = direction(i) + beta * conjugate(i);
02832             }
02833         }
02834 
02835         // compute the unconstrained Newton step
02836         first = 0.0;
02837         second = 0.0;
02838         norm2 = 0.0;
02839         for (i=0; i<dimension; i++)
02840         {
02841             if (active(i)) continue;
02842             double c = conjugate(i);
02843             double Qc = 0.0;
02844             for (j=0; j<dimension; j++)
02845             {
02846                 if (active(j)) continue;
02847                 Qc += quadratic(i, j) * conjugate(j);
02848             }
02849             first += gradient(i) * c;
02850             second += c * Qc;
02851             norm2 += c * c;
02852         }
02853         if (second <= 0.0) break;
02854         step = -first / second;
02855 
02856         // check stopping condition
02857         if (nReset <= 1 && step * step * norm2 < accuracy2) break;
02858 
02859         // clip the step
02860         bound = -1;
02861         clipped = step;
02862         for (i=0; i<dimension; i++)
02863         {
02864             if (active(i)) continue;
02865             if (point(i) + clipped * conjugate(i) > boxMax(i))
02866             {
02867                 clipped = (boxMax(i) - point(i)) / conjugate(i);
02868                 bound = i;
02869             }
02870             else if (point(i) + clipped * conjugate(i) < boxMin(i))
02871             {
02872                 clipped = (boxMin(i) - point(i)) / conjugate(i);
02873                 bound = i;
02874             }
02875         }
02876 
02877         // update the gradient to avoid full recomputation
02878         if (5 * nActive > dimension && nUpdatesLeft > 0)
02879         {
02880             // update the gradient
02881             for (i=0; i<dimension; i++)
02882             {
02883                 if (active(i)) continue;
02884                 double delta = clipped * conjugate(i);
02885                 for (j=0; j<dimension; j++) gradient(j) += quadratic(i, j) * delta;
02886             }
02887             nUpdatesLeft--;
02888         }
02889         else nUpdatesLeft = -1;
02890 
02891         // go!
02892         changed = false;
02893         for (i=0; i<dimension; i++)
02894         {
02895             if (active(i)) continue;
02896             double p = point(i);
02897             double newp = p + clipped * conjugate(i);
02898             point(i) = newp;
02899             if (p != newp) changed = true;
02900         }
02901 
02902         // stop if the step has no numerical effect
02903         if (! changed) break;
02904 
02905         // check the bound carefully
02906         if (bound != -1)
02907         {
02908 //          if (point(bound) > (1.0 - 1e-12) * boxMax(bound)) point(bound) = boxMax(bound);
02909 //          else if (point(bound) < (1.0 + 1e-12) * boxMin(bound)) point(bound) = boxMin(bound);
02910             double threshold = 0.5 * (boxMax(bound) + boxMin(bound));
02911             if (point(bound) > threshold) point(bound) = boxMax(bound);
02912             else point(bound) = boxMin(bound);
02913             nReset = 0;
02914         }
02915     }
02916 
02917     if (iter >= maxiter)
02918     {
02919         throw SHARKEXCEPTION("QpBoxAndEqCG did not converge");
02920         printf("\n\n***************** QpBoxAndEqCG did not converge!!!\n\n");
02921         printf("boxMin:\n");
02922         writeArray(boxMin, std::cout);
02923         printf("boxMax:\n");
02924         writeArray(boxMax, std::cout);
02925         printf("point:\n");
02926         writeArray(point, std::cout);
02927         printf("gradient:\n");
02928         writeArray(gradient, std::cout);
02929         printf("direction:\n");
02930         writeArray(direction, std::cout);
02931         printf("conjugate:\n");
02932         writeArray(conjugate, std::cout);
02933         printf("active:\n");
02934         writeArray(active, std::cout);
02935     }
02936 }
02937 
02938 void QpBoxAndEqCG::Orthogonalize(int oc, const Array<double>& ortho, const Array<bool>& active, ArrayReference<double> vec)
02939 {
02940     int o, d, dim = ortho.dim(1);
02941     double scp, norm;
02942 
02943     // orthogonalize
02944     for (o=0; o<oc; o++)
02945     {
02946         scp = 0.0;
02947         for (d=0; d<dim; d++)
02948         {
02949             if (active(d)) continue;
02950             scp += ortho(o, d) * vec(d);
02951         }
02952         for (d=0; d<dim; d++)
02953         {
02954             if (active(d)) vec(d) = 0.0;
02955             else vec(d) -= scp * ortho(o, d);
02956         }
02957     }
02958 
02959     // normalize
02960     scp = 0.0;
02961     for (d=0; d<dim; d++)
02962     {
02963         if (active(d)) continue;
02964         scp += vec(d) * vec(d);
02965     }
02966     norm = sqrt(scp);
02967     for (d=0; d<dim; d++)
02968     {
02969         if (active(d)) continue;
02970         vec(d) /= norm;
02971     }
02972 }
02973 
02974 void QpBoxAndEqCG::Orthogonalize(Array<double>& eq, const Array<bool>& active)
02975 {
02976     int e, ec = eq.dim(0);
02977     for (e=0; e<ec; e++)
02978     {
02979         Orthogonalize(e, eq, active, eq[e]);
02980     }
02981 }
02982 
02983 void QpBoxAndEqCG::Project(const Array<bool>& active, Array<double>& eq, const Array<double>& gradient, Array<double>& direction)
02984 {
02985     Orthogonalize(eq, active);
02986 
02987     int o, oc = eq.dim(0);
02988     int d, dim = eq.dim(1);
02989     double scp;
02990     for (o=0; o<oc; o++)
02991     {
02992         scp = 0.0;
02993         for (d=0; d<dim; d++)
02994         {
02995             if (active(d)) continue;
02996             scp += eq(o, d) * gradient(d);
02997         }
02998         for (d=0; d<dim; d++)
02999         {
03000             if (active(d)) direction(d) = 0.0;
03001             else direction(d) = gradient(d) - scp * eq(o, d);
03002         }
03003     }
03004 }
03005 
03006 
03008 
03009 
03010 void QpBoxAndEqDecomp::Solve(const Array<double>& quadratic,
03011         const Array<double>& linear,
03012         const Array<double>& boxMin,
03013         const Array<double>& boxMax,
03014         const Array<double>& eqMat,
03015         Array<double>& point,
03016         double accuracy)
03017 {
03018     SIZE_CHECK(quadratic.ndim() == 2);
03019     SIZE_CHECK(linear.ndim() == 1);
03020     SIZE_CHECK(boxMin.ndim() == 1);
03021     SIZE_CHECK(boxMax.ndim() == 1);
03022     SIZE_CHECK(eqMat.ndim() == 2);
03023     SIZE_CHECK(point.ndim() == 1);
03024 
03025     int dimension = quadratic.dim(0);
03026     int equations = eqMat.dim(0);
03027 
03028     SIZE_CHECK((int)quadratic.dim(1) == dimension);
03029     SIZE_CHECK((int)linear.dim(0) == dimension);
03030     SIZE_CHECK((int)boxMin.dim(0) == dimension);
03031     SIZE_CHECK((int)boxMax.dim(0) == dimension);
03032     SIZE_CHECK((int)eqMat.dim(1) == dimension);
03033     SIZE_CHECK((int)point.dim(0) == dimension);
03034 
03035     Array<double> eq(eqMat);
03036     Orthogonalize(eq);
03037 
03038     Array<double> gradient(linear);
03039     Array<double> direction(dimension);
03040     int workingsize = 2 * equations + 1;
03041     int i, j, k;
03042 
03043     Array<int> workingset(workingsize);
03044     Array<double> subQ(workingsize, workingsize);
03045     Array<double> subL(workingsize);
03046     Array<double> subBoxMin(workingsize);
03047     Array<double> subBoxMax(workingsize);
03048     Array<double> subEqMat(equations, workingsize);
03049     Array<double> subPoint(workingsize);
03050 
03051     // initialize the gradient
03052     for (i=0; i<dimension; i++)
03053     {
03054         double p = point(i);
03055         if (p != 0.0)
03056         {
03057             for (j=0; j<dimension; j++) gradient(j) += p * quadratic(i, j);
03058         }
03059     }
03060 
03061     // decomposition loop
03062     while (true)
03063     {
03064         // select a working set
03065         double gNorm1 = 0.0;
03066         {
03067             // project the gradient onto the equality constraints
03068             Project(eq, gradient, direction);
03069 
03070             // find the k largest free components of the direction
03071             for (i=0; i<workingsize; i++)
03072             {
03073                 int best = 0;
03074                 double bestG = 0.0;
03075                 for (j=0; j<dimension; j++)
03076                 {
03077                     double d = direction(j);
03078                     if (point(j) == boxMin(j) && d >= 0.0) continue;
03079                     if (point(j) == boxMax(j) && d <= 0.0) continue;
03080                     d = fabs(d);
03081                     if (d > bestG)
03082                     {
03083                         for (k=0; k<i; k++) if (workingset(k) == j) break;
03084                         if (k == i)
03085                         {
03086                             bestG = d;
03087                             best = j;
03088                         }
03089                     }
03090                 }
03091                 workingset(i) = best;
03092                 gNorm1 += bestG;
03093             }
03094         }
03095 
03096         // check the stopping condition
03097         if (gNorm1 < accuracy) break;
03098 
03099         // define the sub problem and solve it
03100         for (i=0; i<workingsize; i++)
03101         {
03102             j = workingset(i);
03103             for (k=0; k<workingsize; k++) subQ(i, k) = quadratic(j, workingset(k));
03104             subL(i) = linear(j);
03105             subBoxMin(i) = boxMin(j);
03106             subBoxMax(i) = boxMax(j);
03107             for (k=0; k<equations; k++) subEqMat(k, i) = eqMat(k, j);
03108             subPoint(i) = point(j);
03109         }
03110         QpBoxAndEqCG::Solve(subQ, subL, subBoxMin, subBoxMax, subEqMat, subPoint);
03111 
03112         // update point and gradient
03113         for (i=0; i<workingsize; i++)
03114         {
03115             j = workingset(i);
03116             double delta = subPoint(i) - point(j);
03117             if (delta == 0.0) continue;
03118             for (k=0; k<dimension; k++) gradient(k) += delta * quadratic(j, k);
03119             point(j) = subPoint(i);
03120         }
03121     }
03122 }
03123 
03124 void QpBoxAndEqDecomp::Orthogonalize(Array<double>& eq)
03125 {
03126     int e, ec = eq.dim(0);
03127     int o, d, dim = eq.dim(1);
03128     double scp, norm;
03129     for (e=0; e<ec; e++)
03130     {
03131         // orthogonalize
03132         for (o=0; o<e; o++)
03133         {
03134             scp = 0.0;
03135             for (d=0; d<dim; d++)
03136             {
03137                 scp += eq(o, d) * eq(e, d);
03138             }
03139             for (d=0; d<dim; d++)
03140             {
03141                 eq(e, d) -= scp * eq(o, d);
03142             }
03143         }
03144     
03145         // normalize
03146         scp = 0.0;
03147         for (d=0; d<dim; d++)
03148         {
03149             scp += eq(e, d) * eq(e, d);
03150         }
03151         norm = sqrt(scp);
03152         for (d=0; d<dim; d++)
03153         {
03154             eq(e, d) /= norm;
03155         }
03156     }
03157 }
03158 
03159 void QpBoxAndEqDecomp::Project(const Array<double>& eq, const Array<double>& gradient, Array<double>& direction)
03160 {
03161     int o, oc = eq.dim(0);
03162     int d, dim = eq.dim(1);
03163     double scp;
03164     for (o=0; o<oc; o++)
03165     {
03166         scp = 0.0;
03167         for (d=0; d<dim; d++)
03168         {
03169             scp += eq(o, d) * gradient(d);
03170         }
03171         for (d=0; d<dim; d++)
03172         {
03173             direction(d) = gradient(d) - scp * eq(o, d);
03174         }
03175     }
03176 }
03177 
03178 
03180 
03181 
03182 // #define ACCURACY 1e-12
03183 // 
03184 // 
03185 // //
03186 // // perform an affine linear transformation
03187 // // of the given problem with a twofold
03188 // // purpose:
03189 // // 
03190 // // (1) reduce the dimension according to
03191 // //     the equality constraints
03192 // // (2) make the objective functions
03193 // //     isotropic
03194 // //
03195 // QpAffine::QpAffine(
03196 //          const Array<double>& quadratic,
03197 //          const Array<double>& linear,
03198 //          const Array<double>& eqMat,
03199 //          const Array<double>& ineqMat,
03200 //          const Array<double>& ineqVec
03201 //    )
03202 // : quadraticOrig(quadratic)
03203 // , constraintMatOrig(ineqMat)
03204 // , constraintVecOrig(ineqVec)
03205 // {
03206 //  SIZE_CHECK(quadratic.ndim() == 2);
03207 //  SIZE_CHECK(linear.ndim() == 1);
03208 //  SIZE_CHECK(eqMat.ndim() == 2);
03209 //  SIZE_CHECK(ineqMat.ndim() == 2);
03210 //  SIZE_CHECK(ineqVec.ndim() == 1);
03211 // 
03212 //  dimension = quadratic.dim(0);
03213 // 
03214 //  SIZE_CHECK((int)quadratic.dim(1) == dimension);
03215 //  SIZE_CHECK((int)linear.dim(0) == dimension);
03216 //  SIZE_CHECK((int)eqMat.dim(1) == dimension);
03217 //  SIZE_CHECK((int)ineqMat.dim(1) == dimension);
03218 //  SIZE_CHECK(ineqMat.dim(0) == ineqVec.dim(0));
03219 // 
03220 //  int i, j, k;
03221 //  int c, cc;
03222 //  std::vector<double*> list;
03223 // 
03224 //  // compute an orthogonal transformation
03225 //  // s.t. the last k coordinates are within
03226 //  // the equality constraint normal space
03227 //  Array<double> trans1(dimension, dimension);
03228 //  Array<double> inverse1(dimension, dimension);
03229 //  cc = eqMat.dim(0);
03230 //  for (i=dimension-1, c=0; c<cc; c++)
03231 //  {
03232 //      for (j=0; j<dimension; j++) trans1(i, j) = eqMat(c, j);
03233 //      Orthogonalize(list, trans1[i]);
03234 //      if (Normalize(trans1[i]))
03235 //      {
03236 //          list.push_back(&trans1(i, 0));
03237 //          i--;
03238 //      }
03239 //      if ((int)list.size() >= dimension) throw SHARKEXCEPTION("[QpAffine::QpAffine] too many equality constraints");
03240 //  }
03241 //  freedim = i + 1;
03242 //  for (; i >= 0; i--)
03243 //  {
03244 //      do
03245 //      {
03246 //          for (j=0; j<dimension; j++) trans1(i, j) = Rng::gauss();
03247 //          Orthogonalize(list, trans1[i]);
03248 //      }
03249 //      while (! Normalize(trans1[i]));
03250 //      list.push_back(&trans1(i, 0));
03251 //  }
03252 //  inverse1 = trans1;
03253 //  inverse1.transpose();
03254 // 
03255 //  // compute a positive definite transformation
03256 //  // s.t. the quadratic term vanishes on the
03257 //  // last k components and becomes the unit
03258 //  // matrix in the remaining components.
03259 //  Array<double> trans2(freedim, freedim);
03260 //  Array<double> inverse2(freedim, freedim);
03261 //  Array2D<double> tmp(freedim, dimension);
03262 //  Array2D<double> A(freedim, freedim);
03263 //  Array2D<double> U(freedim, freedim);
03264 //  Array2D<double> V(freedim, freedim);
03265 //  Array<double> lambda(freedim);
03266 //  MatMul(trans1, quadratic, tmp);
03267 //  MatMul(tmp, inverse1, A);
03268 //  svd(A, U, V, lambda);
03269 //  for (k=0; k<freedim; k++) lambda(k) = sqrt(lambda(k));
03270 //  V.transpose();
03271 //  for (i=0; i<freedim; i++)
03272 //  {
03273 //      for (j=0; j<freedim; j++)
03274 //      {
03275 //          double value;
03276 //          value = 0.0;
03277 //          for (k=0; k<freedim; k++) value += U(i, k) / lambda(k) * V(k, j);
03278 //          trans2(i, j) = value;
03279 //          value = 0.0;
03280 //          for (k=0; k<freedim; k++) value += U(i, k) * lambda(k) * V(k, j);
03281 //          inverse2(i, j) = value;
03282 //      }
03283 //  }
03284 // 
03285 //  // compute the composed transformation,
03286 //  // its inverse and its transpose
03287 //  transform.resize(freedim, dimension, false);
03288 //  inverse.resize(dimension, freedim, false);
03289 //  MatMul(trans2, trans1, transform);
03290 //  MatMul(inverse1, inverse2, inverse);
03291 // 
03292 //  // transform the objective function and
03293 //  // the inequality constraints
03294 //  this->linear.resize(freedim, false);
03295 //  MatVec(transform, linear, this->linear);
03296 // 
03297 //  cc = ineqMat.dim(0);
03298 //  constraintMatTrans.resize(cc, freedim, false);
03299 //  for (c=0; c<cc; c++) MatVec(transform, ineqMat[c], constraintMatTrans[c]);
03300 // }
03301 // 
03302 // QpAffine::~QpAffine()
03303 // {
03304 // }
03305 // 
03306 // 
03307 // void QpAffine::Solve(Array<double>& point)
03308 // {
03309 //  // strategy:
03310 //  // transform point
03311 //  // loop
03312 //  //   compute direction
03313 //  //   determine active constraints
03314 //  //   transform active constraints into an orthogonal system
03315 //  //   project direction onto constraints
03316 //  //   compute the newton step
03317 //  //   check when we bump into the next inactive constraint
03318 //  //   check if we can reach the optimum, then move there and break
03319 //  //   move to the collision position
03320 //  // end loop
03321 //  // retransform point
03322 // 
03323 //  SIZE_CHECK(point.ndim() == 1);
03324 //  SIZE_CHECK((int)point.dim(0) == dimension);
03325 // 
03326 //  int c, cc = constraintVecOrig.dim(0);
03327 //  int a;
03328 //  int i, j;
03329 //  bool changed;
03330 //  Array<bool> active(cc);
03331 //  std::vector<double*> list;
03332 //  std::vector<double*> single(1);
03333 //  Array<double> ortho(cc, freedim);
03334 //  Array<double> direction(freedim);
03335 //  Array<double> pt(freedim);
03336 //  Array<double> constraintVecTrans(cc);
03337 //  Array<double> linearTrans(freedim);
03338 // 
03339 //  // compute the linear part
03340 //  Array<double> tmp1(dimension);
03341 //  Array<double> tmp2(freedim);
03342 //  MatVec(quadraticOrig, point, tmp1);
03343 //  MatVec(transform, tmp1, tmp2);
03344 //  for (i=0; i<freedim; i++) linearTrans(i) = linear(i) + tmp2(i);
03345 // 
03346 //  // compute the inequality constraint vector
03347 //  for (c=0; c<cc; c++)
03348 //  {
03349 //      double value = constraintVecOrig(c);
03350 //      for (i=0; i<dimension; i++) value += constraintMatOrig(c, i) * point(i);
03351 //      constraintVecTrans(c) = value;
03352 //  }
03353 // 
03354 //  // transform the point
03355 //  pt = 0.0;
03356 // 
03357 //  // loop
03358 //  while (true)
03359 //  {
03360 //      list.clear();
03361 //      a = 0;
03362 // 
03363 //      for (i=0; i<freedim; i++) direction(i) = -(linearTrans(i) + pt(i));
03364 // 
03365 //      active = false;
03366 //      do
03367 //      {
03368 //          changed = false;
03369 //          for (c=0; c<cc; c++)
03370 //          {
03371 //              if (active(c)) continue;
03372 //              double scp = Scp(direction, constraintMatTrans[c]);
03373 //              double value = Scp(pt, constraintMatTrans[c]) + constraintVecTrans(c);
03374 //              if (scp > 0.0 && value >= -ACCURACY)
03375 //              {
03376 //                  active(c) = true;
03377 //                  changed = true;
03378 //                  for (i=0; i<freedim; i++) ortho(a, i) = constraintMatTrans(c, i);
03379 //                  Orthogonalize(list, ortho[a]);
03380 //                  if (Normalize(ortho[a]))
03381 //                  {
03382 //                      list.push_back(&ortho(a, 0));
03383 //                      single[0] = &ortho(a, 0);
03384 //                      Orthogonalize(single, direction);
03385 //                      a++;
03386 //                  }
03387 //              }
03388 //          }
03389 //      }
03390 //      while (changed);
03391 // 
03392 //      if (! Normalize(direction)) break;
03393 // 
03394 //      // compute the newton step
03395 //      double step = 0.0;
03396 //      for (i=0; i<freedim; i++)
03397 //      {
03398 //          step -= (linearTrans(i) + pt(i)) * direction(i);
03399 //      }
03400 //      if (fabs(step) < ACCURACY) break;
03401 // 
03402 //      // check inactive constraints
03403 //      double bestDist = step;
03404 //      for (c=0; c<cc; c++)
03405 //      {
03406 //          if (active(c)) continue;
03407 //          double scp = Scp(direction, constraintMatTrans[c]);
03408 //          if (scp <= 0.0) continue;
03409 //          double t = -(constraintVecTrans(c) + Scp(pt, constraintMatTrans[c])) / scp;
03410 //          if (t < bestDist) bestDist = t;
03411 //      }
03412 // 
03413 //      // move
03414 //      for (i=0; i<freedim; i++) pt(i) += bestDist * direction(i);
03415 // 
03416 //      // check stopping condition
03417 //      if (bestDist == step) break;
03418 //  }
03419 // 
03420 //  // transform the point back
03421 //  for (j=0; j<dimension; j++)
03422 //  {
03423 //      double value = 0.0;
03424 //      for (i=0; i<freedim; i++) value += transform(i, j) * pt(i);
03425 //      point(j) += value;
03426 //  }
03427 // }
03428 // 
03429 // // static
03430 // void QpAffine::Orthogonalize(const std::vector<double*>& ortho, Array<double>& vec)
03431 // {
03432 //  int i, dim = vec.dim(0);
03433 //  int e, ec = ortho.size();
03434 // 
03435 //  // orthogonalize
03436 //  for (e=0; e<ec; e++)
03437 //  {
03438 //      double* o = ortho[e];
03439 //      double scp = 0.0;
03440 //      for (i=0; i<dim; i++) scp += o[i] * vec(i);
03441 //      for (i=0; i<dim; i++) vec(i) -= scp * o[i];
03442 //  }
03443 // }
03444 // 
03445 // // static
03446 // void QpAffine::Orthogonalize(const std::vector<double*>& ortho, ArrayReference<double> vec)
03447 // {
03448 //  int i, dim = vec.dim(0);
03449 //  int e, ec = ortho.size();
03450 // 
03451 //  // orthogonalize
03452 //  for (e=0; e<ec; e++)
03453 //  {
03454 //      double* o = ortho[e];
03455 //      double scp = 0.0;
03456 //      for (i=0; i<dim; i++) scp += o[i] * vec(i);
03457 //      for (i=0; i<dim; i++) vec(i) -= scp * o[i];
03458 //  }
03459 // }
03460 // 
03461 // // static
03462 // bool QpAffine::Normalize(Array<double>& vec)
03463 // {
03464 //  // normalize
03465 //  double len2 = 0.0;
03466 //  int i, dim = vec.dim(0);
03467 //  for (i=0; i<dim; i++)
03468 //  {
03469 //      double entry = vec(i);
03470 //      len2 += entry * entry;
03471 //  }
03472 //  if (len2 == 0.0) return false;
03473 //  double len = sqrt(len2);
03474 //  for (i=0; i<dim; i++) vec(i) /= len;
03475 // 
03476 //  return true;
03477 // }
03478 // 
03479 // // static
03480 // bool QpAffine::Normalize(ArrayReference<double> vec)
03481 // {
03482 //  // normalize
03483 //  double len2 = 0.0;
03484 //  int i, dim = vec.dim(0);
03485 //  for (i=0; i<dim; i++)
03486 //  {
03487 //      double entry = vec(i);
03488 //      len2 += entry * entry;
03489 //  }
03490 //  if (len2 == 0.0) return false;
03491 //  double len = sqrt(len2);
03492 //  for (i=0; i<dim; i++) vec(i) /= len;
03493 // 
03494 //  return true;
03495 // }
03496 // 
03497 // // static
03498 // void QpAffine::MatMul(const Array<double>& M1, const Array<double>& M2, Array<double>& result)
03499 // {
03500 //  SIZE_CHECK(M1.ndim() == 2);
03501 //  SIZE_CHECK(M2.ndim() == 2);
03502 //  SIZE_CHECK(result.ndim() == 2);
03503 // 
03504 //  int i, j, k;
03505 //  int dx = result.dim(1);
03506 //  int dy = result.dim(0);
03507 //  int d = (M1.dim(1) < M2.dim(0)) ? M1.dim(1) : M2.dim(0);
03508 //  for (i=0; i<dy; i++)
03509 //  {
03510 //      for (j=0; j<dx; j++)
03511 //      {
03512 //          double value = 0.0;
03513 //          for (k=0; k<d; k++) value += M1(i, k) * M2(k, j);
03514 //          result(i, j) = value;
03515 //      }
03516 //  }
03517 // }
03518 // 
03519 // // static
03520 // void QpAffine::MatVec(const Array<double>& Mat, const Array<double>& vec, Array<double>& result)
03521 // {
03522 //  SIZE_CHECK(Mat.ndim() == 2);
03523 //  SIZE_CHECK(vec.ndim() == 1);
03524 //  SIZE_CHECK(result.ndim() == 1);
03525 //  SIZE_CHECK(Mat.dim(1) == vec.dim(0));
03526 //  SIZE_CHECK(Mat.dim(0) == result.dim(0));
03527 // 
03528 //  int v, vc = vec.dim(0);
03529 //  int r, rc = result.dim(0);
03530 // 
03531 //  for (r=0; r<rc; r++)
03532 //  {
03533 //      double value = 0.0;
03534 //      for (v=0; v<vc; v++) value += Mat(r, v) * vec(v);
03535 //      result(r) = value;
03536 //  }
03537 // }
03538 // 
03539 // static
03540 // void QpAffine::MatVec(const Array<double>& Mat, const ArrayReference<double> vec, ArrayReference<double> result)
03541 // {
03542 //  SIZE_CHECK(Mat.ndim() == 2);
03543 //  SIZE_CHECK(vec.ndim() == 1);
03544 //  SIZE_CHECK(result.ndim() == 1);
03545 //  SIZE_CHECK(Mat.dim(1) == vec.dim(0));
03546 //  SIZE_CHECK(Mat.dim(0) == result.dim(0));
03547 // 
03548 //  int v, vc = vec.dim(0);
03549 //  int r, rc = result.dim(0);
03550 // 
03551 //  for (r=0; r<rc; r++)
03552 //  {
03553 //      double value = 0.0;
03554 //      for (v=0; v<vc; v++) value += Mat(r, v) * vec(v);
03555 //      result(r) = value;
03556 //  }
03557 // }
03558 // 
03559 // // static
03560 // double QpAffine::Scp(const Array<double>& vec1, ArrayReference<double> vec2)
03561 // {
03562 //  SIZE_CHECK(vec1.ndim() == 1);
03563 //  SIZE_CHECK(vec2.ndim() == 1);
03564 //  SIZE_CHECK(vec1.dim(0) == vec2.dim(0));
03565 // 
03566 //  double ret = 0.0;
03567 //  int i, ic = vec1.dim(0);
03568 //  for (i=0; i<ic; i++)
03569 //  {
03570 //      ret += vec1(i) * vec2(i);
03571 //  }
03572 //  return ret;
03573 // }
  • Shark Main Page
  • Array
  • Rng
  • LinAlg
  • FileUtil
  • EALib
  • MOO-EALib
  • ReClaM
  • Fuzzy
  • Mixture
  • Tutorials
  • FAQ